Merge branch 'main' of https://gitee.com/xia5520/ruoyi-plus-vben5 into hr
This commit is contained in:
commit
2e2cd6d8a7
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -220,7 +220,7 @@
|
|||||||
"commentTranslate.multiLineMerge": true,
|
"commentTranslate.multiLineMerge": true,
|
||||||
"vue.server.hybridMode": true,
|
"vue.server.hybridMode": true,
|
||||||
"vitest.disableWorkspaceWarning": true,
|
"vitest.disableWorkspaceWarning": true,
|
||||||
"cSpell.words": ["tinymce"],
|
"cSpell.words": ["tinymce", "vditor"],
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"editor.linkedEditing": true, // 自动同步更改html标签,
|
"editor.linkedEditing": true, // 自动同步更改html标签,
|
||||||
"vscodeCustomCodeColor.highlightValue": "v-access", // v-access显示的颜色
|
"vscodeCustomCodeColor.highlightValue": "v-access", // v-access显示的颜色
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
- 全局表格加上id 方便进行缓存列排序的操作
|
- 全局表格加上id 方便进行缓存列排序的操作
|
||||||
- 支持菜单名称i18n
|
- 支持菜单名称i18n
|
||||||
- 登录页 验证码登录
|
- 登录页 验证码登录
|
||||||
|
- Markdown编辑/预览组件(基于vditor)
|
||||||
|
|
||||||
**BUG FIXES**
|
**BUG FIXES**
|
||||||
|
|
||||||
|
24
apps/web-antd/src/views/演示使用自行删除/changelog/index.vue
Normal file
24
apps/web-antd/src/views/演示使用自行删除/changelog/index.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { MarkdownPreviewer, Page } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { Skeleton } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import changelog from '../../../../../../CHANGELOG.md?raw';
|
||||||
|
|
||||||
|
const content = ref(changelog);
|
||||||
|
|
||||||
|
const loading = ref(true);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page :auto-content-height="true">
|
||||||
|
<Skeleton v-show="loading" active />
|
||||||
|
<MarkdownPreviewer
|
||||||
|
v-model:value="content"
|
||||||
|
height="100%"
|
||||||
|
@mounted="loading = false"
|
||||||
|
/>
|
||||||
|
</Page>
|
||||||
|
</template>
|
@ -40,6 +40,7 @@
|
|||||||
"@vueuse/integrations": "catalog:",
|
"@vueuse/integrations": "catalog:",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"qrcode": "catalog:",
|
"qrcode": "catalog:",
|
||||||
|
"vditor": "^3.10.6",
|
||||||
"vue": "catalog:",
|
"vue": "catalog:",
|
||||||
"vue-codemirror6": "^1.3.4",
|
"vue-codemirror6": "^1.3.4",
|
||||||
"vue-json-pretty": "^2.4.0",
|
"vue-json-pretty": "^2.4.0",
|
||||||
|
@ -2,6 +2,7 @@ export * from './captcha';
|
|||||||
export * from './code-mirror';
|
export * from './code-mirror';
|
||||||
export * from './ellipsis-text';
|
export * from './ellipsis-text';
|
||||||
export * from './json-preview';
|
export * from './json-preview';
|
||||||
|
export * from './markdown';
|
||||||
export * from './page';
|
export * from './page';
|
||||||
export * from '@vben-core/form-ui';
|
export * from '@vben-core/form-ui';
|
||||||
export * from '@vben-core/popup-ui';
|
export * from '@vben-core/popup-ui';
|
||||||
|
129
packages/effects/common-ui/src/components/markdown/editor.vue
Normal file
129
packages/effects/common-ui/src/components/markdown/editor.vue
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
onBeforeUnmount,
|
||||||
|
onMounted,
|
||||||
|
type PropType,
|
||||||
|
shallowRef,
|
||||||
|
useTemplateRef,
|
||||||
|
watch,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import { usePreferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import Vditor from 'vditor';
|
||||||
|
|
||||||
|
import 'vditor/dist/index.css';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 编辑器高度
|
||||||
|
height: {
|
||||||
|
// string或者number类型
|
||||||
|
type: [String, Number],
|
||||||
|
default: 500,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 编辑模式。默认值: 'wysiwyg'
|
||||||
|
* wysiwyg: 所见即所得
|
||||||
|
* ir: 即时渲染
|
||||||
|
* sv: 分屏预览
|
||||||
|
*/
|
||||||
|
mode: {
|
||||||
|
type: String as PropType<'ir' | 'sv' | 'wysiwyg'>,
|
||||||
|
default: 'wysiwyg',
|
||||||
|
},
|
||||||
|
// 编辑器唯一ID 缓存使用 可记录上次输入
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
|
validator(value, props) {
|
||||||
|
if (!value && props.enableCache) {
|
||||||
|
console.warn('The id is required when enableCache is true');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enableCache: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 禁用编辑器
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 其他配置项
|
||||||
|
options: {
|
||||||
|
type: Object as PropType<IOptions>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
// 初始化 cdn加载完成
|
||||||
|
mounted: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// 挂载节点
|
||||||
|
const vditorRef = useTemplateRef('vditorRef');
|
||||||
|
// 编辑器实例
|
||||||
|
const vditorInstance = shallowRef<null | Vditor>(null);
|
||||||
|
|
||||||
|
// 监听主题切换x
|
||||||
|
const { isDark, locale } = usePreferences();
|
||||||
|
watch(isDark, (dark) => {
|
||||||
|
const theme = dark ? 'dark' : 'light';
|
||||||
|
vditorInstance.value?.setTheme(dark ? 'dark' : 'classic', theme, theme);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 双向绑定
|
||||||
|
const content = defineModel('value', {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听禁用
|
||||||
|
function changeDisabled(disabled: boolean) {
|
||||||
|
if (disabled) {
|
||||||
|
vditorInstance.value?.disabled();
|
||||||
|
} else {
|
||||||
|
vditorInstance.value?.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch(() => props.disabled, changeDisabled);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
vditorInstance.value = new Vditor(vditorRef.value!, {
|
||||||
|
mode: props.mode,
|
||||||
|
value: content.value,
|
||||||
|
height: props.height,
|
||||||
|
lang: locale.value.replace('-', '_') as any,
|
||||||
|
cache: {
|
||||||
|
enable: props.enableCache,
|
||||||
|
id: props.id,
|
||||||
|
},
|
||||||
|
theme: isDark.value ? 'dark' : 'classic',
|
||||||
|
// 手动响应式
|
||||||
|
input(value) {
|
||||||
|
content.value = value;
|
||||||
|
},
|
||||||
|
// 加载完成的事件
|
||||||
|
after() {
|
||||||
|
// 需要初始化就禁用的情况
|
||||||
|
changeDisabled(props.disabled);
|
||||||
|
emit('mounted');
|
||||||
|
},
|
||||||
|
...props.options,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
vditorInstance.value?.destroy();
|
||||||
|
vditorInstance.value = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="vditorRef"></div>
|
||||||
|
</template>
|
@ -0,0 +1,2 @@
|
|||||||
|
export { default as MarkdownEditor } from './editor.vue';
|
||||||
|
export { default as MarkdownPreviewer } from './preview.vue';
|
@ -0,0 +1,94 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
onBeforeUnmount,
|
||||||
|
onMounted,
|
||||||
|
type PropType,
|
||||||
|
shallowRef,
|
||||||
|
useTemplateRef,
|
||||||
|
watch,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import { usePreferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import Vditor from 'vditor';
|
||||||
|
|
||||||
|
import 'vditor/dist/index.css';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 编辑器高度
|
||||||
|
height: {
|
||||||
|
// string或者number类型
|
||||||
|
type: [String, Number],
|
||||||
|
default: 500,
|
||||||
|
},
|
||||||
|
// 其他配置项
|
||||||
|
options: {
|
||||||
|
type: Object as PropType<IOptions>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
// 初始化 cdn加载完成
|
||||||
|
mounted: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// 挂载节点
|
||||||
|
const vditorRef = useTemplateRef('vditorRef');
|
||||||
|
// 编辑器实例
|
||||||
|
const vditorInstance = shallowRef<null | Vditor>(null);
|
||||||
|
|
||||||
|
// 监听主题切换x
|
||||||
|
const { isDark, locale } = usePreferences();
|
||||||
|
watch(isDark, (dark) => {
|
||||||
|
const theme = dark ? 'dark' : 'light';
|
||||||
|
vditorInstance.value?.setTheme(dark ? 'dark' : 'classic', theme, theme);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 双向绑定
|
||||||
|
const content = defineModel('value', {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
vditorInstance.value = new Vditor(vditorRef.value!, {
|
||||||
|
value: content.value,
|
||||||
|
height: props.height,
|
||||||
|
lang: locale.value.replace('-', '_') as any,
|
||||||
|
cache: {
|
||||||
|
enable: false,
|
||||||
|
},
|
||||||
|
theme: isDark.value ? 'dark' : 'classic',
|
||||||
|
// 预览(只读模式) 不显示工具栏
|
||||||
|
toolbar: [],
|
||||||
|
// 手动响应式
|
||||||
|
input(value) {
|
||||||
|
content.value = value;
|
||||||
|
},
|
||||||
|
// 加载完成的事件
|
||||||
|
after() {
|
||||||
|
emit('mounted');
|
||||||
|
// 禁用编辑器
|
||||||
|
vditorInstance.value?.disabled();
|
||||||
|
},
|
||||||
|
...props.options,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
vditorInstance.value?.destroy();
|
||||||
|
vditorInstance.value = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="vditorRef"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.vditor-ir pre.vditor-reset[contenteditable='false'] {
|
||||||
|
cursor: unset;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user