提交 a184c501 编写于 作者: RedPig97's avatar RedPig97

update: 编辑器名称更改

上级 5206e49f
<IbizAutoComplete <app-auto-complete
name="{{item.codeName}}" name="{{item.codeName}}"
:data="state.data" :data="state.data"
{{#if item.valueItemName}} {{#if item.valueItemName}}
......
<IbizCheckboxList <app-checkbox-list
name="{{item.codeName}}" name="{{item.codeName}}"
:data="state.data" :data="state.data"
{{#if item.psEditor.disabled}} {{#if item.psEditor.disabled}}
......
<IbizCheckbox <app-checkbox
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.disabled}} {{#if item.psEditor.disabled}}
:disabled="{{item.psEditor.disabled}}" :disabled="{{item.psEditor.disabled}}"
......
<IbizCode <app-code
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.isReadOnly}} {{#if item.psEditor.isReadOnly}}
:readonly="{{item.psEditor.isReadOnly}}" :readonly="{{item.psEditor.isReadOnly}}"
......
<IbizDataPickerView <app-data-picker-view
name="{{item.codeName}}" name="{{item.codeName}}"
:data="state.data" :data="state.data"
{{#if item.valueItemName}} {{#if item.valueItemName}}
......
<IbizDataPicker <app-data-picker
name="{{item.codeName}}" name="{{item.codeName}}"
:data="state.data" :data="state.data"
{{#if item.valueItemName}} {{#if item.valueItemName}}
......
<IbizDatePicker <app-date-picker
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.placeHolder}} {{#if item.psEditor.placeHolder}}
placeholder="{{item.psEditor.placeHolder}}" placeholder="{{item.psEditor.placeHolder}}"
......
<IbizDropdownList <app-dropdown-list
name="{{item.codeName}}" name="{{item.codeName}}"
:data="state.data"
{{#if item.psEditor.codeList}} {{#if item.psEditor.codeList}}
codeListTag="{{item.psEditor.codeList.codeListTag}}" codeListTag="{{item.psEditor.codeList.codeListTag}}"
codeListType="{{item.psEditor.codeList.codeListType}}" codeListType="{{item.psEditor.codeList.codeListType}}"
...@@ -28,10 +27,12 @@ ...@@ -28,10 +27,12 @@
:viewParams="state.viewParams" :viewParams="state.viewParams"
{{#if (or (eq ctrlType 'form') (eq ctrlType 'panel'))}} {{#if (or (eq ctrlType 'form') (eq ctrlType 'panel'))}}
:value="state.data.{{item.psEditor.name}}" :value="state.data.{{item.psEditor.name}}"
:data="state.data"
@editorEvent="handleEditorEvent" @editorEvent="handleEditorEvent"
{{/if}} {{/if}}
{{#eq ctrlType 'grid'}} {{#eq ctrlType 'grid'}}
:value="record.{{item.psEditor.name}}" :value="record.{{item.psEditor.name}}"
:data="record"
@editorEvent="($event) => handleEditorEvent(index,$event)" @editorEvent="($event) => handleEditorEvent(index,$event)"
{{/eq}} {{/eq}}
/> />
<IbizInputIp <app-input-ip
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.disabled}} {{#if item.psEditor.disabled}}
:disabled="{{item.psEditor.disabled}}" :disabled="{{item.psEditor.disabled}}"
......
<IbizInput <app-input
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.placeHolder}} {{#if item.psEditor.placeHolder}}
placeholder="{{item.psEditor.placeHolder}}" placeholder="{{item.psEditor.placeHolder}}"
......
<IbizListBox <app-list-box
name="{{item.codeName}}" name="{{item.codeName}}"
:data="state.data" :data="state.data"
:server="state.controlService" :server="state.controlService"
......
<IbizRadioGroup <app-radio-group
name="{{item.codeName}}" name="{{item.codeName}}"
:data="state.data" :data="state.data"
{{#if item.psEditor.codeList}} {{#if item.psEditor.codeList}}
......
<IbizRating <app-rating
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.disabled}} {{#if item.psEditor.disabled}}
:disabled="{{item.psEditor.disabled}}" :disabled="{{item.psEditor.disabled}}"
......
<IbizRaw <app-raw
name="{{item.codeName}}" name="{{item.codeName}}"
:date="state.data" :date="state.data"
{{#if item.psEditor.editorParams.contentType}} {{#if item.psEditor.editorParams.contentType}}
......
<IbizSlider <app-slider
name="{{item.codeName}}" name="{{item.codeName}}"
:date="state.data" :date="state.data"
{{#if item.psEditor.disabled}} {{#if item.psEditor.disabled}}
......
<IbizSpan <app-span
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.codeList}} {{#if item.psEditor.codeList}}
codeListTag="{{item.psEditor.codeList.codeListTag}}" codeListTag="{{item.psEditor.codeList.codeListTag}}"
......
<IbizStepper <app-stepper
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.disabled}} {{#if item.psEditor.disabled}}
:disabled="{{item.psEditor.disabled}}" :disabled="{{item.psEditor.disabled}}"
......
<IbizSwitch <app-switch
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.disabled}} {{#if item.psEditor.disabled}}
:disabled="{{item.psEditor.disabled}}" :disabled="{{item.psEditor.disabled}}"
......
<IbizUpload <app-upload
name="{{item.codeName}}" name="{{item.codeName}}"
{{#if item.psEditor.editorParams.method}} {{#if item.psEditor.editorParams.method}}
method="{{item.psEditor.editorParams.method}}" method="{{item.psEditor.editorParams.method}}"
......
<script setup lang="ts">
import { Subject } from 'rxjs';
import axios from 'axios';
import { onMounted, onActivated, onDeactivated, onUnmounted } from "vue";
import { createUUID } from 'qx-util';
import tinymce from 'tinymce/tinymce';
// import 'tinymce/themes/modern';
import 'tinymce/themes/silver';
import 'tinymce/plugins/link';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/table';
import 'tinymce/plugins/image';
import 'tinymce/plugins/imagetools';
import 'tinymce/plugins/codesample';
import 'tinymce/plugins/code';
import 'tinymce/plugins/fullscreen';
import 'tinymce/plugins/preview';
import 'tinymce/plugins/fullscreen';
import 'tinymce/icons/default/icons.min.js';
import { EditorBase, IActionParam, IContext, IParam, UIUtil } from '@core';
const tinymceCode: any = tinymce;
interface RichTextProps {
/**
* @description 编辑器名称
*/
name: string;
/**
* @description 编辑器值
*/
value: string;
/**
* @description 表单数据或表格行数据
*/
data: IParam;
/**
* @description 上下文
*/
context: IContext;
/**
* @description 视图参数
*/
viewParams: IParam;
height: number;
/**
* 上传参数
*
* @type {string}
* @memberof AppRichTextEditor
*/
uploadParams?: any;
/**
* 下载参数
*
* @type {string}
* @memberof AppRichTextEditor
*/
exportParams?: any;
disabled: boolean;
}
interface EditorEmit {
(name: "editorEvent", value: IActionParam): void;
}
const props = withDefaults(defineProps<RichTextProps>(), {
disabled: false,
});
const emit = defineEmits<EditorEmit>();
const { getDownloadUrl, getRequestingHeader, getUploadUrl, getLocal, getUploadParams, getExportParams, getImgURLOfBase64 } = new EditorBase();
const uploadUrl = getUploadUrl();
let downloadUrl = getDownloadUrl();
const headers = getRequestingHeader();
const uploadParams = getUploadParams(props.uploadParams,props.context, props.viewParams,props.data);
const exportParams = getExportParams(props.exportParams,props.context, props.viewParams,props.data);
const uuid: string = createUUID();
const curLocal: string = getLocal();
let richTextEditor: any = null;
let isActivated: boolean = true;
const imgSrcList: any[] = [];
const loadingImgMap: Map<string,any> = new Map();
const successImgMap: Map<string,any> = new Map();
const getImgUrl = (html: any) => {
let imgs:Array<any>|null = html.match(/<img.*?(?:>|\/>)/gi)!=null? html.match(/<img.*?(?:>|\/>)/gi):[];
if(imgs && imgs.length > 0 && imgSrcList && imgSrcList.length > 0){
imgs.forEach((img: any, index: number) => {
var reg = /data:image\/.*;base64,.*(?=[\'\"])/;
if(img.match(reg) != null){
let base64:any = img.match(reg)[0];
const _imgsrc = imgSrcList.find((_imgsrc: any) => Object.is(_imgsrc.key, base64));
if (_imgsrc) {
const newImg = img.replace(reg, '{'+_imgsrc.value+'}');
html = html.replace(img, newImg);
}
}
})
}
return html;
}
const getImgUrlBase64 = async (html: any) => {
let imgs:Array<any>|null = html.match(/<img.*?(?:>|\/>)/gi)!=null? html.match(/<img.*?(?:>|\/>)/gi):[];
if(imgs && imgs.length>0){
for (let item of imgs) {
if(item.match(/src=[\'\"]?([^\'\"]*)[\'\"]?/ig)!=null){
let src:any = item.match(/src=[\'\"]?([^\'\"]*)[\'\"]?/ig)[0];
src = await getImgURLOfBase64(src.substring(5,src.length-1),loadingImgMap,successImgMap);
const image = item.replace(/src=[\'\"]?([^\'\"]*)[\'\"]?/i, 'src="'+src+'"');
html = html.replace(item, image);
}
}
}
return html;
}
const uploadFile = (url: string, formData: any) => {
const subject: Subject<any> = new Subject<any>();
axios({
method: 'post',
url: url,
data: formData,
headers: { 'Content-Type': 'image/png', Accept: 'application/json' },
})
.then((response: any) => {
if (response.status === 200) {
subject.next(response.data);
} else {
subject.error(response);
}
})
.catch((response: any) => {
subject.error(response);
});
return subject;
}
const init = () => {
tinymceCode.init({
selector: '#' + uuid,
width: 'calc( 100% - 2px )',
height: props.height,
min_height: 400,
branding: false,
plugins: [
'link',
'paste',
'table',
'image',
'codesample',
'code',
'fullscreen',
'preview',
// 'quickbars',
'fullscreen',
],
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | link image | preview code fullscreen',
contextmenu: 'cut copy paste pastetext inserttable link ',
// quickbars_insert_toolbar: false,
// quickbars_selection_toolbar: 'forecolor fontsizeselect fontselect',
codesample_languages: [
{ text: 'HTML/XML', value: 'markup' },
{ text: 'JavaScript', value: 'javascript' },
{ text: 'CSS', value: 'css' },
{ text: 'PHP', value: 'php' },
{ text: 'Ruby', value: 'ruby' },
{ text: 'Python', value: 'python' },
{ text: 'Java', value: 'java' },
{ text: 'C', value: 'c' },
{ text: 'C#', value: 'csharp' },
{ text: 'C++', value: 'cpp' },
],
paste_data_images: true,
codesample_content_css: 'assets/tinymce/prism.css',
skin_url: './assets/tinymce/skins/lightgray/ui/oxide',
language_url: './assets/tinymce/langs/' + curLocal + '.js',
language: curLocal,
setup: (editor: any) => {
richTextEditor = editor;
editor.on('blur', () => {
let content = editor.getContent();
content = getImgUrl(content);
emit("editorEvent", {
tag: props.name,
action: "valueChange",
data: content,
});
});
},
images_upload_handler: (bolbinfo: any, success: any, failure: any) => {
const formData = new FormData();
formData.append('file', bolbinfo.blob(), bolbinfo.filename());
let _url = uploadUrl;
if (uploadParams.length > 0) {
_url += '?';
uploadParams.forEach((item: any, i: any) => {
_url += `${Object.keys(item)[0]}=${Object.values(item)[0]}`;
if (i < uploadParams.length - 1) {
_url += '&';
}
});
}
// this.uploadUrl = _url;
uploadFile(_url, formData).subscribe(
(file: any) => {
if (file.filename) {
const id: string = file.fileid;
const url: string = `${downloadUrl}/${id}`;
getImgURLOfBase64(url,loadingImgMap,successImgMap).then((response: any) => {
const imgsrc = imgSrcList.find((imgsrc: any) => Object.is(response,imgsrc.key));
if (!imgsrc) {
const item: any = { key: response, value: file.fileid+file.ext };
imgSrcList.push(item);
}
success(response);
let content = richTextEditor.getContent();
content = getImgUrl(content);
emit("editorEvent", {
tag: props.name,
action: "valueChange",
data: content,
});
});
}
if (exportParams.length > 0) {
downloadUrl += '?';
exportParams.forEach((item: any, i: any) => {
downloadUrl += `${Object.keys(item)[0]}=${Object.values(item)[0]}`;
if (i < exportParams.length - 1) {
downloadUrl += '&';
}
});
}
},
(error: any) => {
console.log(error);
failure('HTTP Error: ' + error.status);
}
);
},
init_instance_callback: (editor: any) => {
richTextEditor = editor;
const url =
downloadUrl.indexOf('../') === 0
? downloadUrl.substring(3)
: downloadUrl;
let value = props.value && props.value.length > 0 ? props.value : '';
value = value.replace(
/\{(\w+)\.(bmp|jpg|jpeg|png|tif|gif|pcx|tga|exif|fpx|svg|psd|cdr|pcd|dxf|ufo|eps|ai|raw|WMF|webp)\}/g,
`${url}/$1`
);
getImgUrlBase64(value).then((newValue: any) =>{
if (richTextEditor) {
richTextEditor.setContent(newValue);
}
if (props.disabled) {
richTextEditor.setMode('readonly');
}
});
},
});
}
onMounted(() => {
init();
});
onActivated(() => {
isActivated = true;
})
onDeactivated(() => {
isActivated = false;
})
onUnmounted(() => {
if (richTextEditor) {
tinymceCode.remove('#' + uuid);
}
})
</script>
<template>
<div :class="['app-editor-container','app-rich-text-editor', `app-rich-text-${name}`]">
<textarea :id="uuid"></textarea>
</div>
</template>
import { toRefs } from 'vue'; import { toRefs } from 'vue';
import { UIUtil, IParam, UIBase } from '@core'; import { UIUtil, IParam, UIBase } from '@core';
import axios from 'axios';
/** /**
* @description 部件基类 * @description 部件基类
* @export * @export
...@@ -180,4 +181,118 @@ export class EditorBase { ...@@ -180,4 +181,118 @@ export class EditorBase {
// } // }
return {}; return {};
} }
/**
* @description 获取上传参数
* @return {*} {IParam[]}
* @memberof EditorBase
*/
public getUploadParams(uploadParams: IParam, data: IParam, context: IParam, viewParams: IParam): IParam[] {
return UIUtil.computedNavData(data, context, viewParams, uploadParams);
}
/**
* @description 获取导出参数
* @return {*} {IParam[]}
* @memberof EditorBase
*/
public getExportParams(exportParams: IParam, data: IParam, context: IParam, viewParams: IParam): IParam[] {
return UIUtil.computedNavData(data, context, viewParams, exportParams);
}
/**
* @description 获取base64的图片url
* @param {string} url
* @param {Map<string, any>} loadingImgMap
* @param {Map<string, any>} successImgMap
* @return {*}
* @memberof EditorBase
*/
public getImgURLOfBase64(url: string, loadingImgMap: Map<string, any>, successImgMap: Map<string, any>): Promise<string> {
return new Promise((resolve, reject) => {
let img = '/';
// 富文本CV上传图片与鼠标移出抛值冲突问题,上传成功回调还没执行时就抛值
var reg = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i;
if (reg.test(url)) {
return resolve(url);
}
// 缓存中有从缓存中拿
if (successImgMap.get(url)) {
let img = successImgMap.get(url);
resolve(img);
}
const callback: Function = (url: string, promise: Promise<any>) => {
promise.then((response: any) => {
if (response && response.status === 200 && response.data) {
// 获取文件名
const disposition = response.headers['content-disposition'];
const filename = disposition.split('filename=')[1];
let type = 'image/png';
if (filename && filename.indexOf('.') > 0) {
const start = filename.lastIndexOf('.');
const expandedName = filename.substring(start + 1);
if (expandedName.match(/(bmp|jpg|jpeg|png|tif|gif|pcx|tga|exif|fpx|svg|psd|cdr|pcd|dxf|ufo|eps|ai|raw|WMF|webp)/gi) != null) {
type = 'image/' + expandedName;
} else {
resolve(img);
}
}
let blob = new Blob([response.data],{type: type});
this.blobToBase64(blob).then((res: any) => {
// 转化后的base64
img = `${res}`;
// 缓存图片
successImgMap.set(url, img);
resolve(img);
})
} else {
resolve(img);
}
}).catch((result: any) => {
return resolve(img);
})
}
// 加载中
if (loadingImgMap.get(url)) {
callback(url, loadingImgMap.get(url));
} else {
let _url = url;
if (!Object.is('/', _url.substring(0,1))) {
_url = '/'+_url;
}
let result:Promise<any> = axios({method: 'get', url: _url, responseType: 'blob'});
loadingImgMap.set(url, result);
callback(url, result);
}
});
}
/**
* @description blob对象转base64
* @param {Blob} blob
* @return {*} {Promise<string>}
* @memberof EditorBase
*/
public blobToBase64(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = (e: any) => {
resolve(e.target.result);
};
fileReader.readAsDataURL(blob);
fileReader.onerror = () => {
reject(new Error('blobToBase64 error'));
};
});
}
/**
* @description 获取当前国际化
* @return {*} {string}
* @memberof EditorBase
*/
public getLocal(): string {
//todo
return 'zh_CN';
}
} }
@use './ibizInput.scss'; @use './app-input.scss';
@use './ibizListBox.scss'; @use './app-list-box.scss';
\ No newline at end of file \ No newline at end of file
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册