Merge branch 'dev' of https://gitee.com/xlsea/ruoyi-plus-soybean into dev
This commit is contained in:
+2
-1
@@ -66,8 +66,9 @@
|
||||
"@sa/materials": "workspace:*",
|
||||
"@sa/utils": "workspace:*",
|
||||
"@types/streamsaver": "^2.0.5",
|
||||
"@umoteam/editor": "^9.0.1",
|
||||
"@vueuse/core": "14.1.0",
|
||||
"@wangeditor-next/editor": "^5.6.49",
|
||||
"@wangeditor-next/editor-for-vue": "^5.1.14",
|
||||
"clipboard": "2.0.11",
|
||||
"dayjs": "1.11.19",
|
||||
"defu": "6.1.4",
|
||||
|
||||
Generated
+581
-2641
File diff suppressed because it is too large
Load Diff
@@ -1,104 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, ref, useAttrs, watch } from 'vue';
|
||||
import type { UmoEditorOptions } from '@umoteam/editor';
|
||||
import { UmoEditor } from '@umoteam/editor';
|
||||
import { fetchBatchDeleteOss, fetchUploadFile } from '@/service/api/system/oss';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
|
||||
defineOptions({
|
||||
name: 'UmoDocEditor'
|
||||
});
|
||||
|
||||
const attrs: UmoEditorOptions = useAttrs();
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
const umoEditorRef = ref<InstanceType<typeof UmoEditor>>();
|
||||
const isSave = ref(false);
|
||||
|
||||
const value = defineModel<string>('value', { required: true, default: '' });
|
||||
|
||||
watch(
|
||||
value,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
if (isSave.value) {
|
||||
isSave.value = false;
|
||||
return;
|
||||
}
|
||||
umoEditorRef.value?.setContent(value.value);
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => appStore.locale,
|
||||
() => {
|
||||
umoEditorRef.value?.setLocale(appStore.locale);
|
||||
}
|
||||
);
|
||||
|
||||
async function handleSave(content: { html: string }) {
|
||||
isSave.value = true;
|
||||
value.value = content.html;
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleFileUpload(file: File) {
|
||||
const { error, data } = await fetchUploadFile(file);
|
||||
if (error) throw new Error(error.message || '上传失败');
|
||||
|
||||
return {
|
||||
id: data.ossId,
|
||||
url: data.url
|
||||
};
|
||||
}
|
||||
|
||||
function handleFileDelete(id: CommonType.IdType) {
|
||||
window.$dialog?.warning({
|
||||
title: '确认删除文件?',
|
||||
content: '文件删除后不可恢复,请确认是否删除!',
|
||||
positiveText: '确认删除',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
const { error } = await fetchBatchDeleteOss([id]);
|
||||
if (error) throw new Error(error.message || '文件删除失败');
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
saveContent: () => umoEditorRef.value?.saveContent()
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="umo-editor size-full">
|
||||
<UmoEditor
|
||||
v-bind="attrs"
|
||||
ref="umoEditorRef"
|
||||
:theme="themeStore.darkMode ? 'dark' : 'light'"
|
||||
@save="handleSave"
|
||||
@file-upload="handleFileUpload"
|
||||
@file-delete="handleFileDelete"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
body .flex-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.umo-editor .flex-center {
|
||||
display: inherit !important;
|
||||
align-items: inherit !important;
|
||||
justify-content: inherit !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,96 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, shallowRef, watch } from 'vue';
|
||||
import '@wangeditor-next/editor/dist/css/style.css';
|
||||
import { Editor, Toolbar } from '@wangeditor-next/editor-for-vue';
|
||||
import { i18nChangeLanguage } from '@wangeditor-next/editor';
|
||||
import type { IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor-next/editor';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { getToken } from '@/store/modules/auth/shared';
|
||||
import { getServiceBaseURL } from '@/utils/service';
|
||||
|
||||
defineOptions({
|
||||
name: 'WangEditor'
|
||||
});
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const editorRef = shallowRef<IDomEditor>();
|
||||
|
||||
const value = defineModel<string>('value', { required: true, default: '' });
|
||||
|
||||
type InsertFnType = (url: string, alt?: string, href?: string) => void;
|
||||
|
||||
const toolbarConfig: Partial<IToolbarConfig> = {};
|
||||
|
||||
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
|
||||
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
|
||||
|
||||
const editorConfig: Partial<IEditorConfig> = {
|
||||
placeholder: '请输入内容...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
server: `${baseURL}/resource/oss/upload`,
|
||||
fieldName: 'file',
|
||||
meta: {},
|
||||
headers: {
|
||||
// @ts-expect-error ignore this type error
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
clientid: import.meta.env.VITE_APP_CLIENT_ID!
|
||||
},
|
||||
metaWithUrl: false,
|
||||
allowedFileTypes: ['image/*'],
|
||||
customInsert(res: any, insertFn: InsertFnType) {
|
||||
if (String(res?.code) !== '200') {
|
||||
window.$message?.error(res?.msg || res);
|
||||
return;
|
||||
}
|
||||
|
||||
insertFn(res.data?.url, res.data?.fieldName);
|
||||
},
|
||||
onSuccess() {},
|
||||
onFailed() {},
|
||||
onError() {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreated = (editor: IDomEditor) => {
|
||||
editorRef.value = editor;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => appStore.locale,
|
||||
() => {
|
||||
const localeMap = {
|
||||
'zh-CN': 'zh-CN',
|
||||
'en-US': 'en'
|
||||
};
|
||||
i18nChangeLanguage(localeMap[appStore.locale]);
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value;
|
||||
if (editor === null) return;
|
||||
|
||||
editorRef.value?.destroy();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard size="small" class="size-full min-h-500px" content-class="size-full">
|
||||
<Toolbar :editor="editorRef" :default-config="toolbarConfig" class="border-b-1px border-gray-200" />
|
||||
<Editor v-model="value" :default-config="editorConfig" @on-created="handleCreated" />
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.w-e-toolbar) {
|
||||
background: inherit !important;
|
||||
border-color: #999 !important;
|
||||
}
|
||||
:deep(.w-e-text-container) {
|
||||
background: inherit;
|
||||
border-color: #999 !important;
|
||||
}
|
||||
</style>
|
||||
Vendored
+2
-4
@@ -16,7 +16,6 @@ declare module 'vue' {
|
||||
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
||||
BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
|
||||
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
||||
copy: typeof import('./../components/custom/dict-radio copy.vue')['default']
|
||||
CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
||||
DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
||||
DataTable: typeof import('./../components/common/data-table.vue')['default']
|
||||
@@ -154,8 +153,8 @@ declare module 'vue' {
|
||||
TableSiderLayout: typeof import('./../components/advanced/table-sider-layout.vue')['default']
|
||||
TenantSelect: typeof import('./../components/custom/tenant-select.vue')['default']
|
||||
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||
UmoDocEditor: typeof import('./../components/custom/umo-doc-editor.vue')['default']
|
||||
UserSelect: typeof import('./../components/custom/user-select.vue')['default']
|
||||
WangEditor: typeof import('./../components/custom/wang-editor.vue')['default']
|
||||
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||
}
|
||||
}
|
||||
@@ -166,7 +165,6 @@ declare global {
|
||||
const BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
||||
const BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
|
||||
const ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
||||
const copy: typeof import('./../components/custom/dict-radio copy.vue')['default']
|
||||
const CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
||||
const DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
||||
const DataTable: typeof import('./../components/common/data-table.vue')['default']
|
||||
@@ -304,7 +302,7 @@ declare global {
|
||||
const TableSiderLayout: typeof import('./../components/advanced/table-sider-layout.vue')['default']
|
||||
const TenantSelect: typeof import('./../components/custom/tenant-select.vue')['default']
|
||||
const ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||
const UmoDocEditor: typeof import('./../components/custom/umo-doc-editor.vue')['default']
|
||||
const UserSelect: typeof import('./../components/custom/user-select.vue')['default']
|
||||
const WangEditor: typeof import('./../components/custom/wang-editor.vue')['default']
|
||||
const WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import type { UmoEditor } from '@umoteam/editor';
|
||||
import { fetchCreateNotice, fetchUpdateNotice } from '@/service/api/system/notice';
|
||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
@@ -29,7 +28,6 @@ const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const umoEditorRef = ref<InstanceType<typeof UmoEditor>>();
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
const { createRequiredRule } = useFormRules();
|
||||
|
||||
@@ -77,7 +75,7 @@ function closeDrawer() {
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
umoEditorRef.value?.saveContent();
|
||||
// umoEditorRef.value?.saveContent();
|
||||
await validate();
|
||||
|
||||
const { noticeId, noticeTitle, noticeType, noticeContent, status } = model.value;
|
||||
@@ -112,7 +110,7 @@ watch(visible, () => {
|
||||
:trap-focus="false"
|
||||
:title="title"
|
||||
display-directive="show"
|
||||
:width="1000"
|
||||
:width="1500"
|
||||
class="max-w-90%"
|
||||
>
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
@@ -129,7 +127,7 @@ watch(visible, () => {
|
||||
</NFormItem>
|
||||
</div>
|
||||
<NFormItem :show-label="false" path="noticeContent">
|
||||
<UmoDocEditor ref="umoEditorRef" v-model:value="model.noticeContent!" />
|
||||
<WangEditor v-model:value="model.noticeContent!" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<template #footer>
|
||||
|
||||
Reference in New Issue
Block a user