diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json
index d7e71449a125ca0643dfd9c7ab237888a67464b6..db404c46646bf408b2c65da2607a51d118c0f564 100644
--- a/apps/web-antd/package.json
+++ b/apps/web-antd/package.json
@@ -43,6 +43,7 @@
"@vben/utils": "workspace:*",
"@vueuse/core": "catalog:",
"ant-design-vue": "catalog:",
+ "vxe-table": "catalog:",
"cropperjs": "catalog:",
"crypto-js": "catalog:",
"dayjs": "catalog:",
diff --git a/apps/web-antd/src/bootstrap.ts b/apps/web-antd/src/bootstrap.ts
index e4aaf40571dc27e81b4c8897114798993a3aba56..3cb24a1add17ee8f4b923d738eced572a5eaee2c 100644
--- a/apps/web-antd/src/bootstrap.ts
+++ b/apps/web-antd/src/bootstrap.ts
@@ -6,6 +6,7 @@ import { preferences } from '@vben/preferences';
import { initStores } from '@vben/stores';
import '@vben/styles';
import '@vben/styles/antd';
+import 'vxe-table/styles/cssvar.scss';
import { useTitle } from '@vueuse/core';
diff --git a/apps/web-antd/src/components/content-wrap/content-wrap.vue b/apps/web-antd/src/components/content-wrap/content-wrap.vue
new file mode 100644
index 0000000000000000000000000000000000000000..026d7d23ff542036b3d3828d4d95cbe976a7811d
--- /dev/null
+++ b/apps/web-antd/src/components/content-wrap/content-wrap.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/apps/web-antd/src/components/content-wrap/index.ts b/apps/web-antd/src/components/content-wrap/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d4f95fddb0838fc99e81a84f30f70a4a1bb97765
--- /dev/null
+++ b/apps/web-antd/src/components/content-wrap/index.ts
@@ -0,0 +1 @@
+export { default as ContentWrap } from './content-wrap.vue';
diff --git a/apps/web-antd/src/components/description/description.vue b/apps/web-antd/src/components/description/description.vue
new file mode 100644
index 0000000000000000000000000000000000000000..41133377832304ba03a915f33b8fcdd594606f63
--- /dev/null
+++ b/apps/web-antd/src/components/description/description.vue
@@ -0,0 +1,71 @@
+
diff --git a/apps/web-antd/src/components/description/index.ts b/apps/web-antd/src/components/description/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a707c486557880c088e1b4a4b0557f224c5ef039
--- /dev/null
+++ b/apps/web-antd/src/components/description/index.ts
@@ -0,0 +1,3 @@
+export { default as Description } from './description.vue';
+export * from './typing';
+export { useDescription } from './use-description';
diff --git a/apps/web-antd/src/components/description/typing.ts b/apps/web-antd/src/components/description/typing.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98e3a52d1d10e22fb23d42555c3e0216a0241fc2
--- /dev/null
+++ b/apps/web-antd/src/components/description/typing.ts
@@ -0,0 +1,18 @@
+import type { DescriptionsProps } from 'ant-design-vue';
+import type { CSSProperties, VNode } from 'vue';
+
+export interface DescriptionItemSchema {
+ label: string | VNode; // 内容的描述
+ field?: string; // 对应 data 中的字段名
+ content?: ((data: any) => string | VNode) | string | VNode; // 自定义需要展示的内容,比如说 dict-tag
+ span?: number; // 包含列的数量
+ labelStyle?: CSSProperties; // 自定义标签样式
+ contentStyle?: CSSProperties; // 自定义内容样式
+ hidden?: ((data: any) => boolean) | boolean; // 是否显示
+}
+
+export interface DescriptionsOptions {
+ data?: Record; // 数据
+ schema?: DescriptionItemSchema[]; // 描述项配置
+ componentProps?: DescriptionsProps; // antd Descriptions 组件参数
+}
diff --git a/apps/web-antd/src/components/description/use-description.ts b/apps/web-antd/src/components/description/use-description.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8cf44ccfaa1b1022c416793772b0e67cb86bc121
--- /dev/null
+++ b/apps/web-antd/src/components/description/use-description.ts
@@ -0,0 +1,70 @@
+import type { DescriptionsOptions } from './typing';
+
+import { defineComponent, h, isReactive, reactive, watch } from 'vue';
+
+import { Description } from './index';
+
+/** 描述列表 api 定义 */
+class DescriptionApi {
+ private state = reactive>({});
+
+ constructor(options: DescriptionsOptions) {
+ this.state = { ...options };
+ }
+
+ getState(): DescriptionsOptions {
+ return this.state as DescriptionsOptions;
+ }
+
+ setState(newState: Partial) {
+ this.state = { ...this.state, ...newState };
+ }
+}
+
+export type ExtendedDescriptionApi = DescriptionApi;
+
+export function useDescription(options: DescriptionsOptions) {
+ const IS_REACTIVE = isReactive(options);
+ const api = new DescriptionApi(options);
+ // 扩展API
+ const extendedApi: ExtendedDescriptionApi = api as never;
+ const Desc = defineComponent({
+ name: 'UseDescription',
+ inheritAttrs: false,
+ setup(_, { attrs, slots }) {
+ // 合并props和attrs到state
+ api.setState({ ...attrs });
+
+ return () =>
+ h(
+ Description,
+ {
+ ...api.getState(),
+ ...attrs,
+ },
+ slots,
+ );
+ },
+ });
+
+ // 响应式支持
+ if (IS_REACTIVE) {
+ watch(
+ () => options.schema,
+ (newSchema) => {
+ api.setState({ schema: newSchema });
+ },
+ { immediate: true, deep: true },
+ );
+
+ watch(
+ () => options.data,
+ (newData) => {
+ api.setState({ data: newData });
+ },
+ { immediate: true, deep: true },
+ );
+ }
+
+ return [Desc, extendedApi] as const;
+}
diff --git a/apps/web-antd/src/utils/dict.ts b/apps/web-antd/src/utils/dict.ts
index adef41445fbbcee80490b47515c9b46449ee6400..59326210ea6b23763f7c1151dc63d581fdeeb363 100644
--- a/apps/web-antd/src/utils/dict.ts
+++ b/apps/web-antd/src/utils/dict.ts
@@ -38,10 +38,7 @@ function getDictObj(dictType: string, value: any) {
* @param dictType 字典类型
* @returns 字典数组
*/
-function getDictOptions(
- dictType: string,
- valueType: 'boolean' | 'number' | 'string' = 'string',
-) {
+function getDictOptions(dictType: string, valueType: 'boolean' | 'number' | 'string' = 'string'): any[] {
const dictOpts = dictStore.getDictOptions(dictType);
const dictOptions: DefaultOptionType = [];
if (dictOpts.length > 0) {
diff --git a/apps/web-antd/src/views/infra/demo/general/demo01/index.vue b/apps/web-antd/src/views/infra/demo/general/demo01/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..01a8b5fab53c0ca82f1459dbce3de17572c905de
--- /dev/null
+++ b/apps/web-antd/src/views/infra/demo/general/demo01/index.vue
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDateTime(row.birthday) }}
+
+
+
+
+
+
+ {{ formatDateTime(row.createTime) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-antd/src/views/infra/demo/general/demo01/modules/form.vue b/apps/web-antd/src/views/infra/demo/general/demo01/modules/form.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8f95c804494a8d9ff9f77e5e90a6154a6f4e19d6
--- /dev/null
+++ b/apps/web-antd/src/views/infra/demo/general/demo01/modules/form.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-antd/src/views/system/notify/my/modules/detail.vue b/apps/web-antd/src/views/system/notify/my/modules/detail.vue
index cdad97daea27c8329a4e7789fb461d1b7e6a3627..2c5275c35ccecca5f0819d99831b49f4a928ebaf 100644
--- a/apps/web-antd/src/views/system/notify/my/modules/detail.vue
+++ b/apps/web-antd/src/views/system/notify/my/modules/detail.vue
@@ -1,18 +1,56 @@
-
-
-
- {{ formData?.templateNickname }}
-
-
- {{ formatDateTime(formData?.createTime) }}
-
-
-
-
-
-
-
-
- {{ formatDateTime(formData?.readTime || '') }}
-
-
- {{ formData?.templateContent }}
-
-
+
+
diff --git a/packages/@core/base/icons/src/lucide.ts b/packages/@core/base/icons/src/lucide.ts
index d6747133b9e89fc298f7abbfd914d030be39fab5..c00f4d9a7adc73cbbbc9ffb004948efb57d37902 100644
--- a/packages/@core/base/icons/src/lucide.ts
+++ b/packages/@core/base/icons/src/lucide.ts
@@ -69,4 +69,5 @@ export {
Upload,
CloudUpload,
History,
+ RefreshCw,
} from 'lucide-vue-next';