修改bug

This commit is contained in:
yangjun 2025-02-05 08:44:07 +08:00
parent 1d8f10c94d
commit 86ea17180e
6 changed files with 423 additions and 7 deletions

View File

@ -20,16 +20,16 @@ VITE_GLOB_API_URL=/jeecg-boot
#后台接口全路径地址(必填) #后台接口全路径地址(必填)
# VITE_GLOB_DOMAIN_URL=https://zxkccx.webvpn.nenu.edu.cn/jeecg-boot # VITE_GLOB_DOMAIN_URL=https://zxkccx.webvpn.nenu.edu.cn/jeecg-boot
VITE_GLOB_DOMAIN_URL=https://smartedu.nenu.edu.cn/jeecg-boot # VITE_GLOB_DOMAIN_URL=https://smartedu.nenu.edu.cn/jeecg-boot
# VITE_GLOB_DOMAIN_URL=https://xxhbtest2.nenu.edu.cn/jeecg-boot VITE_GLOB_DOMAIN_URL=https://xxhbtest2.nenu.edu.cn/jeecg-boot
#VITE_GLOB_DOMAIN_URL=http://210.47.29.177 #VITE_GLOB_DOMAIN_URL=http://210.47.29.177
# VITE_GLOB_DOMAIN_URL=https://kczxcs.nenu.edu.cn/jeecg-boot # VITE_GLOB_DOMAIN_URL=https://kczxcs.nenu.edu.cn/jeecg-boot
# VITE_GLOB_DOMAIN_URL=http://210.47.29.100/jeecg-boot # VITE_GLOB_DOMAIN_URL=http://210.47.29.100/jeecg-boot
#RTC服务器地址 #RTC服务器地址
# VITE_GLOB_RTC_SERVER = https://zxkccx.webvpn.nenu.edu.cn:8081 # VITE_GLOB_RTC_SERVER = https://zxkccx.webvpn.nenu.edu.cn:8081
VITE_GLOB_RTC_SERVER = https://smartedu.nenu.edu.cn:8081 # VITE_GLOB_RTC_SERVER = https://smartedu.nenu.edu.cn:8081
# VITE_GLOB_RTC_SERVER = https://xxhbtest2.nenu.edu.cn:8081 VITE_GLOB_RTC_SERVER = https://xxhbtest2.nenu.edu.cn:8081
# 接口父路径前缀 # 接口父路径前缀
VITE_GLOB_API_URL_PREFIX= VITE_GLOB_API_URL_PREFIX=

View File

@ -0,0 +1,39 @@
<template>
<Tinymce v-bind="bindProps" @change="onChange" />
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { Tinymce } from '/@/components/Tinymce/index3';
import { propTypes } from '/@/utils/propTypes';
export default defineComponent({
name: 'JEditor',
// attrs html
inheritAttrs: false,
components: { Tinymce },
props: {
value: propTypes.string.def(''),
disabled: propTypes.bool.def(false),
},
emits: ['change', 'update:value'],
setup(props, { emit, attrs }) {
// props attrs
const bindProps = computed(() => Object.assign({}, props, attrs));
// value change
function onChange(value) {
emit('change', value);
emit('update:value', value);
}
return {
bindProps,
onChange,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,4 @@
import { withInstall } from '/@/utils/index';
import tinymce from './src/Editor3.vue';
export const Tinymce = withInstall(tinymce);

View File

@ -0,0 +1,366 @@
<template>
<div :class="prefixCls" :style="{ width: containerWidth }">
<textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }" v-if="!initOptions.inline"></textarea>
<slot v-else></slot>
</div>
</template>
<script lang="ts">
import type { Editor, RawEditorSettings } from 'tinymce';
import tinymce from 'tinymce/tinymce';
import 'tinymce/themes/silver';
import 'tinymce/icons/default/icons';
import 'tinymce/plugins/advlist';
import 'tinymce/plugins/anchor';
import 'tinymce/plugins/autolink';
import 'tinymce/plugins/autosave';
import 'tinymce/plugins/code';
import 'tinymce/plugins/codesample';
import 'tinymce/plugins/directionality';
import 'tinymce/plugins/fullscreen';
import 'tinymce/plugins/hr';
import 'tinymce/plugins/insertdatetime';
import 'tinymce/plugins/link';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/media';
import 'tinymce/plugins/nonbreaking';
import 'tinymce/plugins/noneditable';
import 'tinymce/plugins/pagebreak';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/preview';
import 'tinymce/plugins/print';
import 'tinymce/plugins/save';
import 'tinymce/plugins/searchreplace';
import 'tinymce/plugins/spellchecker';
import 'tinymce/plugins/tabfocus';
// import 'tinymce/plugins/table';
import 'tinymce/plugins/template';
import 'tinymce/plugins/textpattern';
import 'tinymce/plugins/visualblocks';
import 'tinymce/plugins/visualchars';
import 'tinymce/plugins/wordcount';
import 'tinymce/plugins/image';
import 'tinymce/plugins/table';
import 'tinymce/plugins/textcolor';
import 'tinymce/plugins/contextmenu';
// import '/@/components/Tinymce/plugins/kityformula-editor';
import { defineComponent, computed, nextTick, ref, unref, watch, onDeactivated, onBeforeUnmount } from 'vue';
import ImgUpload from './ImgUpload.vue';
import { toolbar, plugins, simplePlugins, simpleToolbar, menubar } from './tinymce';
import { buildShortUUID } from '/@/utils/uuid';
import { bindHandlers } from './helper';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { useDesign } from '/@/hooks/web/useDesign';
import { isNumber } from '/@/utils/is';
import { useLocale } from '/@/locales/useLocale';
import { useAppStore } from '/@/store/modules/app';
import { uploadFile } from '/@/api/common/api';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
const tinymceProps = {
options: {
type: Object as PropType<Partial<RawEditorSettings>>,
default: {},
},
value: {
type: String,
},
toolbar: {
type: [Array as PropType<string[]>, String],
default: toolbar,
},
plugins: {
type: Array as PropType<string[]>,
default: plugins,
},
menubar: {
type: [Object, String],
default: menubar,
},
modelValue: {
type: String,
},
height: {
type: [Number, String] as PropType<string | number>,
required: false,
default: 220,
},
width: {
type: [Number, String] as PropType<string | number>,
required: false,
default: 'auto',
},
showImageUpload: {
type: Boolean,
default: true,
},
};
export default defineComponent({
name: 'Tinymce',
components: { ImgUpload },
inheritAttrs: false,
props: tinymceProps,
emits: ['change', 'update:modelValue', 'inited', 'init-error'],
setup(props, { emit, attrs }) {
const showImageUpload = ref(false);
const editorRef = ref<Nullable<Editor>>(null);
const fullscreen = ref(false);
const tinymceId = ref<string>(buildShortUUID('tiny-vue'));
const elRef = ref<Nullable<HTMLElement>>(null);
const { prefixCls } = useDesign('tinymce-container');
const appStore = useAppStore();
const tinymceContent = computed(() => props.modelValue);
const containerWidth = computed(() => {
const width = props.width;
if (isNumber(width)) {
return `${width}px`;
}
return width;
});
const skinName = computed(() => {
return appStore.getDarkMode === 'light' ? 'jeecg' : 'oxide-dark';
});
const langName = computed(() => {
const lang = useLocale().getLocale.value;
return ['zh_CN', 'en'].includes(lang) ? lang : 'zh_CN';
});
const initOptions = computed((): RawEditorSettings => {
const { height, options, toolbar, plugins, menubar } = props;
const publicPath = import.meta.env.VITE_PUBLIC_PATH || '/';
return {
selector: `#${unref(tinymceId)}`,
height,
toolbar: [...toolbar],
menubar: menubar,
plugins,
// external_plugins: {
// 'kityformula-editor': publicPath + 'resource/tinymce/plugins/kityformula-editor/plugin.min.js'
// },
language_url: publicPath + 'resource/tinymce/langs/' + langName.value + '.js',
language: langName.value,
branding: false,
default_link_target: '_blank',
link_title: false,
object_resizing: true,
toolbar_mode: 'sliding',
auto_focus: true,
toolbar_groups: true,
skin: skinName.value,
skin_url: publicPath + 'resource/tinymce/skins/ui/' + skinName.value,
images_upload_handler: (blobInfo, success) => {
let params = {
file: blobInfo.blob(),
filename: blobInfo.filename(),
data: { biz: 'jeditor', jeditor: '1' },
};
const uploadSuccess = (res) => {
if (res.success) {
if (res.message == 'local') {
const img = 'data:image/jpeg;base64,' + blobInfo.base64();
success(img);
} else {
let img = getFileAccessHttpUrl(res.message);
success(img);
}
}
};
uploadFile(params, uploadSuccess);
},
content_css: publicPath + 'resource/tinymce/skins/ui/' + skinName.value + '/content.min.css',
...options,
setup: (editor: Editor) => {
editorRef.value = editor;
editor.ui.registry.addButton('customImageUpload', {
text: 'Insert Image',
onAction: () => {
// ImgUpload
showImageUpload.value = true;
}
});
editor.on('init', (e) => initSetup(e));
},
};
});
const disabled = computed(() => {
const { options } = props;
const getdDisabled = options && Reflect.get(options, 'readonly');
const editor = unref(editorRef);
// update-begin-author:taoyan date:20220407 for: disabled
if (editor) {
editor.setMode(getdDisabled || attrs.disabled === true ? 'readonly' : 'design');
}
if (attrs.disabled === true) {
return true;
}
// update-end-author:taoyan date:20220407 for: disabled
return getdDisabled ?? false;
});
watch(
() => attrs.disabled,
() => {
const editor = unref(editorRef);
if (!editor) {
return;
}
editor.setMode(attrs.disabled ? 'readonly' : 'design');
}
);
onMountedOrActivated(() => {
if (!initOptions.value.inline) {
tinymceId.value = buildShortUUID('tiny-vue');
}
nextTick(() => {
setTimeout(() => {
initEditor();
}, 30);
});
});
onBeforeUnmount(() => {
destory();
});
onDeactivated(() => {
destory();
});
function destory() {
if (tinymce !== null) {
tinymce?.remove?.(unref(initOptions).selector!);
}
}
function initEditor() {
const el = unref(elRef);
if (el) {
el.style.visibility = '';
}
tinymce
.init(unref(initOptions))
.then((editor) => {
emit('inited', editor);
})
.catch((err) => {
emit('init-error', err);
});
}
function initSetup(e) {
const editor = unref(editorRef);
if (!editor) {
return;
}
const value = props.modelValue || '';
editor.setContent(value);
bindModelHandlers(editor);
bindHandlers(e, attrs, unref(editorRef));
}
function setValue(editor: Recordable, val: string, prevVal?: string) {
if (editor && typeof val === 'string' && val !== prevVal && val !== editor.getContent({ format: attrs.outputFormat })) {
editor.setContent(val);
}
}
function bindModelHandlers(editor: any) {
const modelEvents = attrs.modelEvents ? attrs.modelEvents : null;
const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(' ') : modelEvents;
watch(
() => props.modelValue,
(val: string, prevVal: string) => {
setValue(editor, val, prevVal);
}
);
watch(
() => props.value,
(val: string, prevVal: string) => {
setValue(editor, val, prevVal);
},
{
immediate: true,
}
);
editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => {
const content = editor.getContent({ format: attrs.outputFormat });
emit('update:modelValue', content);
emit('change', content);
});
editor.on('FullscreenStateChanged', (e) => {
fullscreen.value = e.state;
});
}
function handleImageUploading(name: string) {
const editor = unref(editorRef);
if (!editor) {
return;
}
editor.execCommand('mceInsertContent', false, getUploadingImgName(name));
const content = editor?.getContent() ?? '';
setValue(editor, content);
}
function handleDone(name: string, url: string) {
const editor = unref(editorRef);
if (!editor) {
return;
}
const content = editor?.getContent() ?? '';
const val = content?.replace(getUploadingImgName(name), `<img src="${url}"/>`) ?? '';
setValue(editor, val);
}
function getUploadingImgName(name: string) {
return `[uploading:${name}]`;
}
return {
prefixCls,
containerWidth,
initOptions,
tinymceContent,
elRef,
tinymceId,
handleImageUploading,
handleDone,
editorRef,
fullscreen,
disabled,
};
},
});
</script>
<style lang="less" scoped></style>
<style lang="less">
@prefix-cls: ~'@{namespace}-tinymce-container';
.@{prefix-cls} {
position: relative;
line-height: normal;
textarea {
z-index: -1;
visibility: hidden;
}
}
</style>

View File

@ -23,6 +23,10 @@ export const plugins = [
'advlist anchor autolink autosave code codesample directionality fullscreen hr insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus template textpattern visualblocks visualchars wordcount image', 'advlist anchor autolink autosave code codesample directionality fullscreen hr insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus template textpattern visualblocks visualchars wordcount image',
]; ];
export const plugins2 = [
'advlist anchor autolink autosave code codesample directionality fullscreen hr insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus template textpattern visualblocks visualchars wordcount image',
];
// export const toolbar = // export const toolbar =
// 'fullscreen code preview | undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent lineheight|subscript superscript blockquote| numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | insertfile image media pageembed link anchor codesample insertdatetime hr| a11ycheck ltr rtl'; // 'fullscreen code preview | undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent lineheight|subscript superscript blockquote| numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | insertfile image media pageembed link anchor codesample insertdatetime hr| a11ycheck ltr rtl';
@ -31,6 +35,7 @@ export const plugins = [
// fullscreen code preview | undo redo | // fullscreen code preview | undo redo |
export const simplePlugins = ['lists image link media table textcolor wordcount contextmenu fullscreen']; export const simplePlugins = ['lists image link media table textcolor wordcount contextmenu fullscreen'];
export const simple2Plugins = [''];
export const simpleToolbar = [ export const simpleToolbar = [
'undo redo formatselect bold italic alignleft aligncenter alignright alignjustify bullist numlist outdent indent', 'undo redo formatselect bold italic alignleft aligncenter alignright alignjustify bullist numlist outdent indent',

View File

@ -134,7 +134,8 @@
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24">
<a-form-item label="作业要求" v-bind="validateInfos.content"> <a-form-item label="作业要求" v-bind="validateInfos.content">
<j-editor v-model:value="zyInfo.content" v-if="zyyqShow" @blur="handleZyyqShow(0)" /> <!-- <j-editor v-model:value="zyInfo.content" v-if="zyyqShow" @blur="handleZyyqShow(0)" /> -->
<a-textarea v-model:value="zyInfo.content" v-if="zyyqShow" @blur="handleZyyqShow(0)"></a-textarea>
<div style="color: #777777" v-html="zyInfo.content" v-if="!zyyqShow"></div> <div style="color: #777777" v-html="zyInfo.content" v-if="!zyyqShow"></div>
<div @click="handleZyyqShow(1)" class="tishi" v-if="!editDisabled">温馨提示点击可编辑作业要求</div> <div @click="handleZyyqShow(1)" class="tishi" v-if="!editDisabled">温馨提示点击可编辑作业要求</div>
<!-- <div @click="handleZyyqShow(1)"><span style="cursor: pointer; color: #18a689">温馨提示点击可编辑作业要求</span></div> --> <!-- <div @click="handleZyyqShow(1)"><span style="cursor: pointer; color: #18a689">温馨提示点击可编辑作业要求</span></div> -->
@ -153,7 +154,8 @@
placeholder="请选择评分标准是否允许学生查看" :disabled="editDisabled" /> placeholder="请选择评分标准是否允许学生查看" :disabled="editDisabled" />
</a-form-item> </a-form-item>
<a-form-item label=""> <a-form-item label="">
<j-editor v-model:value="zyInfo.pfbz" v-if="pfbzShow" @blur="handlePfbzShow(0)" /> <!-- <j-editor v-model:value="zyInfo.pfbz" v-if="pfbzShow" @blur="handlePfbzShow(0)" /> -->
<a-textarea v-model:value="zyInfo.pfbz" v-if="pfbzShow" @blur="handlePfbzShow(0)"></a-textarea>
<div v-html="zyInfo.pfbz" v-if="!pfbzShow"></div> <div v-html="zyInfo.pfbz" v-if="!pfbzShow"></div>
<div class="tishi" @click="handlePfbzShow(1)" v-if="!editDisabled">温馨提示点击可编辑评分标准</div> <div class="tishi" @click="handlePfbzShow(1)" v-if="!editDisabled">温馨提示点击可编辑评分标准</div>
</a-form-item> </a-form-item>
@ -582,7 +584,7 @@ import { Form } from 'ant-design-vue';
import { saveOrUpdate } from '/@/views/zy/zyInfo/ZyInfo.api'; import { saveOrUpdate } from '/@/views/zy/zyInfo/ZyInfo.api';
import { getValueType } from '/@/utils'; import { getValueType } from '/@/utils';
import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue'; import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
import JEditor from '/@/components/Form/src/jeecg/components/JEditor.vue'; import JEditor from '/@/components/Form/src/jeecg/components/JEditor3.vue';
import dayjs, { Dayjs } from 'dayjs'; import dayjs, { Dayjs } from 'dayjs';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { newcolumns } from '/@/views/zy/zyInfoStudent/ZyInfoStudent.data'; import { newcolumns } from '/@/views/zy/zyInfoStudent/ZyInfoStudent.data';