diff --git a/apps/web-antd/src/components/dict/index.ts b/apps/web-antd/src/components/dict/index.ts
new file mode 100644
index 00000000..9dd84d24
--- /dev/null
+++ b/apps/web-antd/src/components/dict/index.ts
@@ -0,0 +1,5 @@
+import { withInstall } from '#/utils';
+
+import dictTag from './src/index.vue';
+
+export const DictTag = withInstall(dictTag);
diff --git a/apps/web-antd/src/components/dict/src/data.tsx b/apps/web-antd/src/components/dict/src/data.tsx
new file mode 100644
index 00000000..68395f8a
--- /dev/null
+++ b/apps/web-antd/src/components/dict/src/data.tsx
@@ -0,0 +1,44 @@
+import { type VNode } from 'vue';
+
+import { Tag } from 'ant-design-vue';
+
+interface TagType {
+ [key: string]: { color: string; label: string };
+}
+
+export const tagTypes: TagType = {
+ cyan: { color: 'cyan', label: 'cyan' },
+ danger: { color: 'error', label: '危险(danger)' },
+ /** 由于和elementUI不同 用于替换颜色 */
+ default: { color: 'default', label: '默认(default)' },
+ green: { color: 'green', label: 'green' },
+ info: { color: 'default', label: '信息(info)' },
+ orange: { color: 'orange', label: 'orange' },
+ /** 自定义预设 color可以为16进制颜色 */
+ pink: { color: 'pink', label: 'pink' },
+ primary: { color: 'processing', label: '主要(primary)' },
+ purple: { color: 'purple', label: 'purple' },
+ red: { color: 'red', label: 'red' },
+ success: { color: 'success', label: '成功(success)' },
+ warning: { color: 'warning', label: '警告(warning)' },
+};
+
+// 字典选择使用 { label: string; value: string }[]
+interface Options {
+ label: string | VNode;
+ value: string;
+}
+
+export function tagSelectOptions() {
+ const selectArray: Options[] = [];
+ Object.keys(tagTypes).forEach((key) => {
+ if (!tagTypes[key]) return;
+ const label = tagTypes[key].label;
+ const color = tagTypes[key].color;
+ selectArray.push({
+ label: {label},
+ value: key,
+ });
+ });
+ return selectArray;
+}
diff --git a/apps/web-antd/src/components/dict/src/index.vue b/apps/web-antd/src/components/dict/src/index.vue
new file mode 100644
index 00000000..2505d19b
--- /dev/null
+++ b/apps/web-antd/src/components/dict/src/index.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
{{ label }}
+
{{ label }}
+
+
diff --git a/apps/web-antd/src/components/tinymce/index.ts b/apps/web-antd/src/components/tinymce/index.ts
new file mode 100644
index 00000000..bc00d6b2
--- /dev/null
+++ b/apps/web-antd/src/components/tinymce/index.ts
@@ -0,0 +1,5 @@
+import { withInstall } from '#/utils';
+
+import tinymce from './src/editor.vue';
+
+export const Tinymce = withInstall(tinymce);
diff --git a/apps/web-antd/src/components/tinymce/src/editor.vue b/apps/web-antd/src/components/tinymce/src/editor.vue
new file mode 100644
index 00000000..e4a8e2dd
--- /dev/null
+++ b/apps/web-antd/src/components/tinymce/src/editor.vue
@@ -0,0 +1,370 @@
+
+
+
+
+
+
+
diff --git a/apps/web-antd/src/components/tinymce/src/helper.ts b/apps/web-antd/src/components/tinymce/src/helper.ts
new file mode 100644
index 00000000..e169f501
--- /dev/null
+++ b/apps/web-antd/src/components/tinymce/src/helper.ts
@@ -0,0 +1,85 @@
+const validEvents = new Set([
+ 'onActivate',
+ 'onAddUndo',
+ 'onBeforeAddUndo',
+ 'onBeforeExecCommand',
+ 'onBeforeGetContent',
+ 'onBeforeRenderUI',
+ 'onBeforeSetContent',
+ 'onBeforePaste',
+ 'onBlur',
+ 'onChange',
+ 'onClearUndos',
+ 'onClick',
+ 'onContextMenu',
+ 'onCopy',
+ 'onCut',
+ 'onDblclick',
+ 'onDeactivate',
+ 'onDirty',
+ 'onDrag',
+ 'onDragDrop',
+ 'onDragEnd',
+ 'onDragGesture',
+ 'onDragOver',
+ 'onDrop',
+ 'onExecCommand',
+ 'onFocus',
+ 'onFocusIn',
+ 'onFocusOut',
+ 'onGetContent',
+ 'onHide',
+ 'onInit',
+ 'onKeyDown',
+ 'onKeyPress',
+ 'onKeyUp',
+ 'onLoadContent',
+ 'onMouseDown',
+ 'onMouseEnter',
+ 'onMouseLeave',
+ 'onMouseMove',
+ 'onMouseOut',
+ 'onMouseOver',
+ 'onMouseUp',
+ 'onNodeChange',
+ 'onObjectResizeStart',
+ 'onObjectResized',
+ 'onObjectSelected',
+ 'onPaste',
+ 'onPostProcess',
+ 'onPostRender',
+ 'onPreProcess',
+ 'onProgressState',
+ 'onRedo',
+ 'onRemove',
+ 'onReset',
+ 'onSaveContent',
+ 'onSelectionChange',
+ 'onSetAttrib',
+ 'onSetContent',
+ 'onShow',
+ 'onSubmit',
+ 'onUndo',
+ 'onVisualAid',
+]);
+
+const isValidKey = (key: string) => validEvents.has(key);
+
+export const bindHandlers = (
+ initEvent: Event,
+ listeners: any,
+ editor: any,
+): void => {
+ Object.keys(listeners)
+ .filter((element) => isValidKey(element))
+ .forEach((key: string) => {
+ const handler = listeners[key];
+ if (typeof handler === 'function') {
+ if (key === 'onInit') {
+ handler(initEvent, editor);
+ } else {
+ editor.on(key.slice(2), (e: any) => handler(e, editor));
+ }
+ }
+ });
+};
diff --git a/apps/web-antd/src/components/tinymce/src/img-upload.vue b/apps/web-antd/src/components/tinymce/src/img-upload.vue
new file mode 100644
index 00000000..441b9fee
--- /dev/null
+++ b/apps/web-antd/src/components/tinymce/src/img-upload.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
diff --git a/apps/web-antd/src/components/tinymce/src/tinymce.ts b/apps/web-antd/src/components/tinymce/src/tinymce.ts
new file mode 100644
index 00000000..eb3964a6
--- /dev/null
+++ b/apps/web-antd/src/components/tinymce/src/tinymce.ts
@@ -0,0 +1,11 @@
+// Any plugins you want to setting has to be imported
+// Detail plugins list see https://www.tinymce.com/docs/plugins/
+// Custom builds see https://www.tinymce.com/download/custom-builds/
+// colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration
+
+// quickbars 快捷栏
+export const plugins =
+ 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap emoticons accordion';
+
+export const toolbar =
+ 'undo redo | accordion accordionremove | blocks fontfamily fontsize | bold italic underline strikethrough | align numlist bullist | link image | table media | lineheight outdent indent| forecolor backcolor removeformat | charmap emoticons | code fullscreen preview | save print | pagebreak anchor codesample | ltr rtl';