Optimize structure
This commit is contained in:
@ -34,7 +34,6 @@
|
|||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"node_modules/perfect-scrollbar/css/perfect-scrollbar.css",
|
"node_modules/perfect-scrollbar/css/perfect-scrollbar.css",
|
||||||
"node_modules/quill/dist/quill.snow.css",
|
|
||||||
"src/styles.less",
|
"src/styles.less",
|
||||||
{
|
{
|
||||||
"input": "src/styles/default.less",
|
"input": "src/styles/default.less",
|
||||||
@ -49,7 +48,6 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"node_modules/quill/dist/quill.min.js",
|
|
||||||
"node_modules/perfect-scrollbar/dist/perfect-scrollbar.js",
|
"node_modules/perfect-scrollbar/dist/perfect-scrollbar.js",
|
||||||
"node_modules/qrious/dist/qrious.min.js"
|
"node_modules/qrious/dist/qrious.min.js"
|
||||||
],
|
],
|
||||||
@ -139,7 +137,6 @@
|
|||||||
"karmaConfig": "karma.conf.js",
|
"karmaConfig": "karma.conf.js",
|
||||||
"tsConfig": "tsconfig.spec.json",
|
"tsConfig": "tsconfig.spec.json",
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"node_modules/quill/dist/quill.min.js",
|
|
||||||
"node_modules/perfect-scrollbar/dist/perfect-scrollbar.js"
|
"node_modules/perfect-scrollbar/dist/perfect-scrollbar.js"
|
||||||
],
|
],
|
||||||
"styles": [],
|
"styles": [],
|
||||||
|
|||||||
4745
package-lock.json
generated
4745
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,8 @@
|
|||||||
"hmr": "ng s -o --hmr",
|
"hmr": "ng s -o --hmr",
|
||||||
"build": "npm run ng-high-memory build --",
|
"build": "npm run ng-high-memory build --",
|
||||||
"build:dev": "npm run build -- -c dev",
|
"build:dev": "npm run build -- -c dev",
|
||||||
"build:test": "npm run build -- -c test",
|
"build:test": "npm run build -- -c test --stats-json",
|
||||||
|
"bundle-report": "webpack-bundle-analyzer dist/stats.json",
|
||||||
"analyze": "npm run ng-high-memory build -- --source-map",
|
"analyze": "npm run ng-high-memory build -- --source-map",
|
||||||
"analyze:view": "source-map-explorer dist/**/*.js",
|
"analyze:view": "source-map-explorer dist/**/*.js",
|
||||||
"lint": "npm run lint:ts && npm run lint:style",
|
"lint": "npm run lint:ts && npm run lint:style",
|
||||||
@ -67,8 +68,6 @@
|
|||||||
"ngx-trend": "^7.0.0",
|
"ngx-trend": "^7.0.0",
|
||||||
"perfect-scrollbar": "^1.5.2",
|
"perfect-scrollbar": "^1.5.2",
|
||||||
"qrious": "^4.0.2",
|
"qrious": "^4.0.2",
|
||||||
"quill": "^1.3.7",
|
|
||||||
"quill-image-resize-module": "^3.0.0",
|
|
||||||
"rxjs": "~6.6.0",
|
"rxjs": "~6.6.0",
|
||||||
"screenfull": "^5.1.0",
|
"screenfull": "^5.1.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
@ -124,7 +123,8 @@
|
|||||||
"stylelint-declaration-block-no-ignored-properties": "^2.4.0",
|
"stylelint-declaration-block-no-ignored-properties": "^2.4.0",
|
||||||
"stylelint-order": "^4.1.0",
|
"stylelint-order": "^4.1.0",
|
||||||
"ts-node": "~8.3.0",
|
"ts-node": "~8.3.0",
|
||||||
"typescript": "~4.3.5"
|
"typescript": "~4.3.5",
|
||||||
|
"webpack-bundle-analyzer": "^4.5.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"(src)/**/*.{html,ts}": [
|
"(src)/**/*.{html,ts}": [
|
||||||
|
|||||||
@ -1,417 +0,0 @@
|
|||||||
/**
|
|
||||||
* Part of the code comes from https://github.com/KillerCodeMonkey/ngx-quill/
|
|
||||||
*/
|
|
||||||
import { DOCUMENT, isPlatformServer } from '@angular/common';
|
|
||||||
import {
|
|
||||||
AfterViewInit,
|
|
||||||
Component,
|
|
||||||
ElementRef,
|
|
||||||
EventEmitter,
|
|
||||||
forwardRef,
|
|
||||||
Inject,
|
|
||||||
Input,
|
|
||||||
NgZone,
|
|
||||||
OnChanges,
|
|
||||||
OnDestroy,
|
|
||||||
Output,
|
|
||||||
PLATFORM_ID,
|
|
||||||
Renderer2,
|
|
||||||
SimpleChanges
|
|
||||||
} from '@angular/core';
|
|
||||||
import { ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms';
|
|
||||||
import { ModalHelper } from '@delon/theme';
|
|
||||||
import { BooleanInput, InputBoolean, InputNumber, NumberInput } from '@delon/util';
|
|
||||||
import ImageResize from 'quill-image-resize-module';
|
|
||||||
|
|
||||||
import { FileManagerImgComponent } from '../file-manager/file-manager-img.component';
|
|
||||||
|
|
||||||
declare var Quill: any;
|
|
||||||
const Delta = require('quill-delta');
|
|
||||||
|
|
||||||
export interface CustomOption {
|
|
||||||
import: string;
|
|
||||||
whitelist: any[];
|
|
||||||
}
|
|
||||||
|
|
||||||
Quill.register('modules/imageResize', ImageResize);
|
|
||||||
['align', 'background', 'color', 'direction', 'font'].forEach(type => Quill.register(Quill.import(`attributors/style/${type}`), true));
|
|
||||||
const Size = Quill.import('attributors/style/size');
|
|
||||||
const VALUES = {
|
|
||||||
// NOTICE: Should be sync modify `@ql-sizes` in `styles/fix/_quill.less`
|
|
||||||
size: ['10px', '12px', '14px', '16px', '18px', '20px', '24px']
|
|
||||||
};
|
|
||||||
Size.whitelist = VALUES.size;
|
|
||||||
Quill.register(Size, true);
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'editor',
|
|
||||||
template: ``,
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: NG_VALUE_ACCESSOR,
|
|
||||||
useExisting: forwardRef(() => EditorComponent),
|
|
||||||
multi: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: NG_VALIDATORS,
|
|
||||||
useExisting: forwardRef(() => EditorComponent),
|
|
||||||
multi: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
host: {
|
|
||||||
'[class.quill-editor]': 'true'
|
|
||||||
},
|
|
||||||
preserveWhitespaces: false
|
|
||||||
})
|
|
||||||
export class EditorComponent implements AfterViewInit, ControlValueAccessor, OnChanges, OnDestroy, Validator {
|
|
||||||
@Input()
|
|
||||||
set mode(value: 'full' | 'simple') {
|
|
||||||
this._mode = value;
|
|
||||||
const handlers = {
|
|
||||||
image: (state: boolean) => this.image(state)
|
|
||||||
};
|
|
||||||
if (value === 'full') {
|
|
||||||
this.modules = {
|
|
||||||
imageResize: {},
|
|
||||||
toolbar: {
|
|
||||||
handlers,
|
|
||||||
container: [
|
|
||||||
['bold', 'italic', 'underline', 'strike'], // toggled buttons
|
|
||||||
['blockquote', 'code-block'],
|
|
||||||
|
|
||||||
[{ header: 1 }, { header: 2 }], // custom button values
|
|
||||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
||||||
[{ script: 'sub' }, { script: 'super' }], // superscript/subscript
|
|
||||||
[{ indent: '-1' }, { indent: '+1' }], // outdent/indent
|
|
||||||
// [{ direction: 'rtl' }], // text direction
|
|
||||||
|
|
||||||
[{ size: VALUES.size }], // custom dropdown
|
|
||||||
[{ header: [1, 2, 3, false] }],
|
|
||||||
|
|
||||||
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
|
|
||||||
// [{ font: [] }],
|
|
||||||
[{ align: [] }],
|
|
||||||
|
|
||||||
['clean'], // remove formatting button
|
|
||||||
|
|
||||||
['link', 'image', 'video'] // link and image, video
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.modules = {
|
|
||||||
imageResize: {},
|
|
||||||
toolbar: {
|
|
||||||
handlers,
|
|
||||||
container: [
|
|
||||||
['bold', 'italic', 'underline', 'strike', 'blockquote'], // toggled buttons
|
|
||||||
[{ header: 1 }, { header: 2 }], // custom button values
|
|
||||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
||||||
[{ header: [1, 2, 3, false] }],
|
|
||||||
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
|
|
||||||
[{ align: [] }],
|
|
||||||
['clean'], // remove formatting button
|
|
||||||
['link', 'image', 'video'] // link and image, video
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private elementRef: ElementRef,
|
|
||||||
@Inject(DOCUMENT) private doc: any,
|
|
||||||
@Inject(PLATFORM_ID) private platformId: {},
|
|
||||||
private renderer: Renderer2,
|
|
||||||
private zone: NgZone,
|
|
||||||
private modalHelper: ModalHelper
|
|
||||||
) {}
|
|
||||||
static ngAcceptInputType_readOnly: BooleanInput;
|
|
||||||
static ngAcceptInputType_maxLength: NumberInput;
|
|
||||||
static ngAcceptInputType_minLength: NumberInput;
|
|
||||||
static ngAcceptInputType_required: BooleanInput;
|
|
||||||
static ngAcceptInputType_strict: BooleanInput;
|
|
||||||
|
|
||||||
quill: any;
|
|
||||||
editorElem!: HTMLElement;
|
|
||||||
emptyArray: any[] = [];
|
|
||||||
content: any;
|
|
||||||
selectionChangeEvent: any;
|
|
||||||
textChangeEvent: any;
|
|
||||||
_mode!: 'full' | 'simple';
|
|
||||||
|
|
||||||
@Input() format: 'object' | 'html' | 'text' | 'json' = 'html';
|
|
||||||
@Input() theme?: string;
|
|
||||||
@Input() modules?: { [index: string]: any };
|
|
||||||
@Input() @InputBoolean() readOnly?: boolean;
|
|
||||||
@Input() placeholder = '';
|
|
||||||
@Input() @InputNumber() maxLength?: number;
|
|
||||||
@Input() @InputNumber() minLength?: number;
|
|
||||||
@Input() @InputBoolean() required?: boolean;
|
|
||||||
@Input() formats?: string[];
|
|
||||||
@Input() style: any = { height: '250px' };
|
|
||||||
@Input() @InputBoolean() strict = true;
|
|
||||||
@Input() scrollingContainer?: HTMLElement | string;
|
|
||||||
@Input() bounds?: HTMLElement | string;
|
|
||||||
@Input() customOptions: CustomOption[] = [];
|
|
||||||
|
|
||||||
@Output() readonly editorCreated = new EventEmitter();
|
|
||||||
@Output() readonly contentChanged = new EventEmitter();
|
|
||||||
@Output() readonly selectionChanged = new EventEmitter();
|
|
||||||
private image(_: boolean): void {
|
|
||||||
this.modalHelper
|
|
||||||
.create(
|
|
||||||
FileManagerImgComponent,
|
|
||||||
{
|
|
||||||
opt: {
|
|
||||||
multiple: true,
|
|
||||||
i: { orderby: 0, cat_id: 0, ps: 20 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
size: 1000,
|
|
||||||
modalOptions: {
|
|
||||||
nzClosable: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.subscribe((res: any[]) => {
|
|
||||||
// delete
|
|
||||||
const range = this.quill.getSelection(true);
|
|
||||||
this.quill.updateContents(new Delta().retain(range.index).delete(range.length));
|
|
||||||
// install all images
|
|
||||||
for (const ii of res) {
|
|
||||||
this.quill.updateContents(new Delta().retain(range.index).delete(range.length).insert({ image: ii.mp }, { alt: ii.title }));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
valueGetter = (quillEditor: any, editorElement: HTMLElement): any => {
|
|
||||||
let html: string | null = editorElement.children[0].innerHTML;
|
|
||||||
if (html === '<p><br></p>' || html === '<div><br><div>') {
|
|
||||||
html = null;
|
|
||||||
}
|
|
||||||
let modelValue = html;
|
|
||||||
|
|
||||||
if (this.format === 'text') {
|
|
||||||
modelValue = quillEditor.getText();
|
|
||||||
} else if (this.format === 'object') {
|
|
||||||
modelValue = quillEditor.getContents();
|
|
||||||
} else if (this.format === 'json') {
|
|
||||||
try {
|
|
||||||
modelValue = JSON.stringify(quillEditor.getContents());
|
|
||||||
} catch (e) {
|
|
||||||
modelValue = quillEditor.getText();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return modelValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
valueSetter = (quillEditor: any, value: any, _format: 'object' | 'html' | 'json'): any => {
|
|
||||||
if (this.format === 'html') {
|
|
||||||
return quillEditor.clipboard.convert(value);
|
|
||||||
} else if (this.format === 'json') {
|
|
||||||
try {
|
|
||||||
return JSON.parse(value);
|
|
||||||
} catch (e) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
onModelChange = (_: any) => {};
|
|
||||||
onModelTouched = () => {};
|
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
|
||||||
if (isPlatformServer(this.platformId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._mode == null) {
|
|
||||||
this.mode = 'full';
|
|
||||||
}
|
|
||||||
|
|
||||||
const modules: any = this.modules;
|
|
||||||
|
|
||||||
this.elementRef.nativeElement.insertAdjacentHTML('beforeend', '<div quill-editor-element></div>');
|
|
||||||
this.editorElem = this.elementRef.nativeElement.querySelector('[quill-editor-element]');
|
|
||||||
|
|
||||||
if (this.style) {
|
|
||||||
Object.keys(this.style).forEach((key: string) => {
|
|
||||||
this.renderer.setStyle(this.editorElem, key, this.style[key]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.customOptions.forEach(customOption => {
|
|
||||||
const newCustomOption = Quill.import(customOption.import);
|
|
||||||
newCustomOption.whitelist = customOption.whitelist;
|
|
||||||
Quill.register(newCustomOption, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.quill = new Quill(this.editorElem, {
|
|
||||||
modules,
|
|
||||||
placeholder: this.placeholder,
|
|
||||||
readOnly: this.readOnly || false,
|
|
||||||
theme: this.theme || 'snow',
|
|
||||||
formats: this.formats,
|
|
||||||
bounds: this.bounds ? (this.bounds === 'self' ? this.editorElem : this.bounds) : this.doc.body,
|
|
||||||
strict: this.strict,
|
|
||||||
scrollingContainer: this.scrollingContainer
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.content) {
|
|
||||||
if (this.format === 'object') {
|
|
||||||
this.quill.setContents(this.content, 'silent');
|
|
||||||
} else if (this.format === 'text') {
|
|
||||||
this.quill.setText(this.content, 'silent');
|
|
||||||
} else if (this.format === 'json') {
|
|
||||||
try {
|
|
||||||
this.quill.setContents(JSON.parse(this.content), 'silent');
|
|
||||||
} catch (e) {
|
|
||||||
this.quill.setText(this.content, 'silent');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const contents = this.quill.clipboard.convert(this.content);
|
|
||||||
this.quill.setContents(contents, 'silent');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.quill.history.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editorCreated.emit(this.quill);
|
|
||||||
|
|
||||||
// mark model as touched if editor lost focus
|
|
||||||
this.selectionChangeEvent = this.quill.on('selection-change', (range: any, oldRange: any, source: string) => {
|
|
||||||
this.zone.run(() => {
|
|
||||||
this.selectionChanged.emit({
|
|
||||||
editor: this.quill,
|
|
||||||
range,
|
|
||||||
oldRange,
|
|
||||||
source
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!range) {
|
|
||||||
this.onModelTouched();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// update model if text changes
|
|
||||||
this.textChangeEvent = this.quill.on('text-change', (delta: any, oldDelta: any, source: string) => {
|
|
||||||
const text = this.quill.getText();
|
|
||||||
const content = this.quill.getContents();
|
|
||||||
|
|
||||||
let html: string | null = this.editorElem.children[0].innerHTML;
|
|
||||||
if (html === '<p><br></p>' || html === '<div><br><div>') {
|
|
||||||
html = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.zone.run(() => {
|
|
||||||
this.onModelChange(this.valueGetter(this.quill, this.editorElem));
|
|
||||||
|
|
||||||
this.contentChanged.emit({
|
|
||||||
editor: this.quill,
|
|
||||||
html,
|
|
||||||
text,
|
|
||||||
content,
|
|
||||||
delta,
|
|
||||||
oldDelta,
|
|
||||||
source
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
if (this.selectionChangeEvent) {
|
|
||||||
this.selectionChangeEvent.removeListener('selection-change');
|
|
||||||
}
|
|
||||||
if (this.textChangeEvent) {
|
|
||||||
this.textChangeEvent.removeListener('text-change');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
if (!this.quill) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (changes.readOnly) {
|
|
||||||
this.quill.enable(!changes.readOnly.currentValue);
|
|
||||||
}
|
|
||||||
if (changes.placeholder) {
|
|
||||||
this.quill.root.dataset.placeholder = changes.placeholder.currentValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeValue(currentValue: any): void {
|
|
||||||
this.content = currentValue;
|
|
||||||
|
|
||||||
if (this.quill) {
|
|
||||||
if (currentValue) {
|
|
||||||
if (this.format === 'text') {
|
|
||||||
this.quill.setText(currentValue);
|
|
||||||
} else {
|
|
||||||
this.quill.setContents(this.valueSetter(this.quill, this.content, this.format));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.quill.setText('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerOnChange(fn: (value: any) => void): void {
|
|
||||||
this.onModelChange = fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerOnTouched(fn: () => void): void {
|
|
||||||
this.onModelTouched = fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
validate(): any {
|
|
||||||
if (!this.quill) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const err: {
|
|
||||||
minLengthError?: { given: number; minLength: number };
|
|
||||||
maxLengthError?: { given: number; maxLength: number };
|
|
||||||
requiredError?: { empty: boolean };
|
|
||||||
} = {};
|
|
||||||
let valid = true;
|
|
||||||
|
|
||||||
const textLength = this.quill.getText().trim().length;
|
|
||||||
|
|
||||||
if (this.minLength && textLength && textLength < this.minLength) {
|
|
||||||
err.minLengthError = {
|
|
||||||
given: textLength,
|
|
||||||
minLength: this.minLength
|
|
||||||
};
|
|
||||||
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.maxLength && textLength > this.maxLength) {
|
|
||||||
err.maxLengthError = {
|
|
||||||
given: textLength,
|
|
||||||
maxLength: this.maxLength
|
|
||||||
};
|
|
||||||
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.required && !textLength) {
|
|
||||||
err.requiredError = {
|
|
||||||
empty: true
|
|
||||||
};
|
|
||||||
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid ? null : err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
|
|
||||||
import { FileManagerModule } from '../file-manager';
|
|
||||||
import { EditorComponent } from './editor.component';
|
|
||||||
|
|
||||||
const COMPONENTS = [EditorComponent];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [FileManagerModule],
|
|
||||||
declarations: COMPONENTS,
|
|
||||||
exports: COMPONENTS
|
|
||||||
})
|
|
||||||
export class EditorModule {}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
---
|
|
||||||
order: 60
|
|
||||||
title: editor
|
|
||||||
type: Component
|
|
||||||
---
|
|
||||||
|
|
||||||
Based on [quill](https://github.com/quilljs/quill) WYSIWYG editor, [DEMO](https://preview.ng-alain.com/pro/#/ec/ware/edit/10001).
|
|
||||||
|
|
||||||
## Feature
|
|
||||||
|
|
||||||
- Integration [file-manager](file-manager)
|
|
||||||
- Integration [quill-image-resize-module](https://github.com/kensnyder/quill-image-resize-module)
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
| Property | Description | Type | Default |
|
|
||||||
| ---------------------- | -------------------------------------- | ------------------- | --------------------- |
|
|
||||||
| `[(ngModel)]` | Value of quill | `string` | - |
|
|
||||||
| `[mode]` | Mode of quill | `full,simple` | `full` |
|
|
||||||
| `[theme]` | Theme of quill | `string` | `snow` |
|
|
||||||
| `[readOnly]` | Whether to readonly | `boolean` | `false` |
|
|
||||||
| `[required]` | Whether to required | `boolean` | - |
|
|
||||||
| `[maxLength]` | The maximum number of quill characters | `number` | - |
|
|
||||||
| `[minLength]` | The minimum number of quill characters | `number` | - |
|
|
||||||
| `[placeholder]` | Placeholder of quill | `string` | - |
|
|
||||||
| `[style]` | Styles of quill | `any` | `{ height: '250px' }` |
|
|
||||||
| `(editorCreated)` | Quill rendered event | `EventEmitter<any>` | - |
|
|
||||||
| `(contentChanged)` | Quill content change event | `EventEmitter<any>` | - |
|
|
||||||
| `(selectionChanged)` | `selection-change` event | `EventEmitter<any>` | - |
|
|
||||||
|
|
||||||
## sf widget
|
|
||||||
|
|
||||||
Widget name: `editor`.
|
|
||||||
|
|
||||||
### ui
|
|
||||||
|
|
||||||
| Property | Description | Type | Default |
|
|
||||||
| -------------------- | -------------------------- | ------------------------- | ------- |
|
|
||||||
| `(contentChanged)` | Quill content change event | `(value: string) => void` | - |
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
export * from './editor.component';
|
|
||||||
export * from './editor.module';
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
---
|
|
||||||
order: 60
|
|
||||||
title: editor
|
|
||||||
type: Component
|
|
||||||
---
|
|
||||||
|
|
||||||
基于 [quill](https://github.com/quilljs/quill) 富文本编辑器,参考[示例](https://preview.ng-alain.com/pro/#/ec/ware/edit/10001)。
|
|
||||||
|
|
||||||
## 特性
|
|
||||||
|
|
||||||
- 整合 [file-manager](file-manager)
|
|
||||||
- 整合 [quill-image-resize-module](https://github.com/kensnyder/quill-image-resize-module)
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
|
||||||
| ---------------------- | -------------------------- | ------------------- | --------------------- |
|
|
||||||
| `[(ngModel)]` | 值 | `string` | - |
|
|
||||||
| `[mode]` | 模式 | `full,simple` | `full` |
|
|
||||||
| `[theme]` | 主题 | `string` | `snow` |
|
|
||||||
| `[readOnly]` | 是否只读 | `boolean` | `false` |
|
|
||||||
| `[required]` | 是否必填 | `boolean` | - |
|
|
||||||
| `[maxLength]` | 最大长度 | `number` | - |
|
|
||||||
| `[minLength]` | 最少长度 | `number` | - |
|
|
||||||
| `[placeholder]` | 文本框默认值 | `string` | - |
|
|
||||||
| `[style]` | 样式,可以决定富文本的高度 | `any` | `{ height: '250px' }` |
|
|
||||||
| `(editorCreated)` | 初始化完成后事件 | `EventEmitter<any>` | - |
|
|
||||||
| `(contentChanged)` | 内容变更事件 | `EventEmitter<any>` | - |
|
|
||||||
| `(selectionChanged)` | `selection-change` 事件 | `EventEmitter<any>` | - |
|
|
||||||
|
|
||||||
## sf 小部件
|
|
||||||
|
|
||||||
小部件名称:`editor`。
|
|
||||||
|
|
||||||
### ui
|
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
|
||||||
| -------------------- | ------------ | ------------------------- | ------ |
|
|
||||||
| `(contentChanged)` | 内容变更事件 | `(value: string) => void` | - |
|
|
||||||
@ -8,7 +8,6 @@
|
|||||||
*/
|
*/
|
||||||
// Modules
|
// Modules
|
||||||
export * from './components/delay/index';
|
export * from './components/delay/index';
|
||||||
export * from './components/editor/index';
|
|
||||||
export * from './components/file-manager/index';
|
export * from './components/file-manager/index';
|
||||||
export * from './components/masonry/index';
|
export * from './components/masonry/index';
|
||||||
export * from './components/mouse-focus/index';
|
export * from './components/mouse-focus/index';
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
* @Description: 全局事件监听服务
|
* @Description: 全局事件监听服务
|
||||||
*/
|
*/
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import * as EventEmitter from 'eventemitter3';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
|
|||||||
@ -9,10 +9,7 @@
|
|||||||
* Copyright (C) 2022 huzhenhong. All rights reserved.
|
* Copyright (C) 2022 huzhenhong. All rights reserved.
|
||||||
*/
|
*/
|
||||||
/* eslint-disable import/order */
|
/* eslint-disable import/order */
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { DelonACLModule } from '@delon/acl';
|
import { DelonACLModule } from '@delon/acl';
|
||||||
import { DelonFormModule } from '@delon/form';
|
import { DelonFormModule } from '@delon/form';
|
||||||
import { AlainThemeModule } from '@delon/theme';
|
import { AlainThemeModule } from '@delon/theme';
|
||||||
@ -24,7 +21,6 @@ import { SHARED_ZORRO_MODULES } from './shared-zorro.module';
|
|||||||
import { PRO_SHARED_MODULES } from '../layout/pro';
|
import { PRO_SHARED_MODULES } from '../layout/pro';
|
||||||
import { AddressModule } from './components/address';
|
import { AddressModule } from './components/address';
|
||||||
import { DelayModule } from './components/delay';
|
import { DelayModule } from './components/delay';
|
||||||
import { EditorModule } from './components/editor';
|
|
||||||
import { FileManagerModule } from './components/file-manager';
|
import { FileManagerModule } from './components/file-manager';
|
||||||
import { MasonryModule } from './components/masonry';
|
import { MasonryModule } from './components/masonry';
|
||||||
import { MouseFocusModule } from './components/mouse-focus';
|
import { MouseFocusModule } from './components/mouse-focus';
|
||||||
@ -34,22 +30,18 @@ import { SharedThirdModule } from './shared-third.module';
|
|||||||
import { LogisticsTimeLineComponent } from './components/logistics-time-line/logistics-time-line.component';
|
import { LogisticsTimeLineComponent } from './components/logistics-time-line/logistics-time-line.component';
|
||||||
import { AmapModule } from './components/amap/amap.module';
|
import { AmapModule } from './components/amap/amap.module';
|
||||||
import { ImageListModule } from './components/imagelist';
|
import { ImageListModule } from './components/imagelist';
|
||||||
import { DictSelectComponent } from './components/dict-select';
|
|
||||||
import { PipeModule } from './pipes';
|
import { PipeModule } from './pipes';
|
||||||
import { AccountDetailComponent } from './components/account-detail/account-detail.component';
|
import { AccountDetailComponent } from './components/account-detail/account-detail.component';
|
||||||
import { CaptchaModule } from './components/captcha';
|
import { CaptchaModule } from './components/captcha';
|
||||||
import { rebateTableModule } from './components/rebate-table';
|
import { rebateTableModule } from './components/rebate-table';
|
||||||
import { SearchDrawerModule } from './components/search-drawer';
|
import { SearchDrawerModule } from './components/search-drawer';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
|
||||||
import { BasicModuleModule } from './basic-module.module';
|
import { BasicModuleModule } from './basic-module.module';
|
||||||
import { STWidgetModule } from './widget/st-widget.module';
|
import { STWidgetModule } from './widget/st-widget.module';
|
||||||
import { GlobalConfigModule } from '../global-config.module';
|
|
||||||
// import { SearchDrawerComponent } from './components/search-drawer/search-drawer.component';
|
// import { SearchDrawerComponent } from './components/search-drawer/search-drawer.component';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
AddressModule,
|
AddressModule,
|
||||||
DelayModule,
|
DelayModule,
|
||||||
EditorModule,
|
|
||||||
FileManagerModule,
|
FileManagerModule,
|
||||||
MasonryModule,
|
MasonryModule,
|
||||||
MouseFocusModule,
|
MouseFocusModule,
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { ControlWidget } from '@delon/form';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'sf-editor',
|
|
||||||
template: `
|
|
||||||
<sf-item-wrap [id]="id" [schema]="schema" [ui]="ui" [showError]="showError" [error]="error" [showTitle]="schema.title">
|
|
||||||
<editor [ngModel]="value" name="sf.editor" (ngModelChange)="_change($event)"></editor>
|
|
||||||
</sf-item-wrap>
|
|
||||||
`,
|
|
||||||
preserveWhitespaces: false,
|
|
||||||
})
|
|
||||||
// tslint:disable-next-line: component-class-suffix
|
|
||||||
export class EditorWidget extends ControlWidget {
|
|
||||||
static readonly KEY = 'editor';
|
|
||||||
|
|
||||||
_change(value: string): void {
|
|
||||||
this.setValue(value);
|
|
||||||
if (this.ui.contentChanged) {
|
|
||||||
this.ui.contentChanged(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,7 +11,7 @@ import { NgModule } from '@angular/core';
|
|||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { STWidgetRegistry } from '@delon/abc/st';
|
import { STWidgetRegistry } from '@delon/abc/st';
|
||||||
import { DelonFormModule, WidgetRegistry } from '@delon/form';
|
import { DelonFormModule, WidgetRegistry } from '@delon/form';
|
||||||
import { AddressModule, EditorModule, FileManagerModule } from '@shared';
|
import { AddressModule, FileManagerModule } from '@shared';
|
||||||
import { NzButtonModule } from 'ng-zorro-antd/button';
|
import { NzButtonModule } from 'ng-zorro-antd/button';
|
||||||
import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
|
import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
|
||||||
import { NzDividerModule } from 'ng-zorro-antd/divider';
|
import { NzDividerModule } from 'ng-zorro-antd/divider';
|
||||||
@ -25,7 +25,6 @@ import { SharedModule } from '../shared.module';
|
|||||||
import { AddressWidget } from './address/address.widget';
|
import { AddressWidget } from './address/address.widget';
|
||||||
import { STCurrencyCHYWidget } from './currency-cny/curreny-cny.widget';
|
import { STCurrencyCHYWidget } from './currency-cny/curreny-cny.widget';
|
||||||
import { DictSelectWidget } from './dict-select/dict-select.widget';
|
import { DictSelectWidget } from './dict-select/dict-select.widget';
|
||||||
import { EditorWidget } from './editor/editor.widget';
|
|
||||||
import { SLFromToSearchWidget } from './from-to-search/from-to-search.widget';
|
import { SLFromToSearchWidget } from './from-to-search/from-to-search.widget';
|
||||||
import { EAFromToWidget } from './from-to/from-to.widget';
|
import { EAFromToWidget } from './from-to/from-to.widget';
|
||||||
import { ImgWidget } from './img/img.widget';
|
import { ImgWidget } from './img/img.widget';
|
||||||
@ -38,7 +37,6 @@ import { SharedThirdModule } from '../shared-third.module';
|
|||||||
import { DictSelectComponent } from '../components/dict-select/dict-select.component';
|
import { DictSelectComponent } from '../components/dict-select/dict-select.component';
|
||||||
|
|
||||||
export const STWIDGET_COMPONENTS = [
|
export const STWIDGET_COMPONENTS = [
|
||||||
EditorWidget,
|
|
||||||
ImgWidget,
|
ImgWidget,
|
||||||
AddressWidget,
|
AddressWidget,
|
||||||
TinymceWidget,
|
TinymceWidget,
|
||||||
@ -61,7 +59,6 @@ export const STWIDGET_COMPONENTS = [
|
|||||||
SharedThirdModule,
|
SharedThirdModule,
|
||||||
DelonFormModule.forRoot(),
|
DelonFormModule.forRoot(),
|
||||||
AddressModule,
|
AddressModule,
|
||||||
EditorModule,
|
|
||||||
FileManagerModule,
|
FileManagerModule,
|
||||||
NzDividerModule,
|
NzDividerModule,
|
||||||
NzStepsModule,
|
NzStepsModule,
|
||||||
@ -76,7 +73,7 @@ export const STWIDGET_COMPONENTS = [
|
|||||||
})
|
})
|
||||||
export class STWidgetModule {
|
export class STWidgetModule {
|
||||||
constructor(widgetRegistry: WidgetRegistry, sTWidgetRegistry: STWidgetRegistry) {
|
constructor(widgetRegistry: WidgetRegistry, sTWidgetRegistry: STWidgetRegistry) {
|
||||||
widgetRegistry.register(EditorWidget.KEY, EditorWidget);
|
// widgetRegistry.register(EditorWidget.KEY, EditorWidget);
|
||||||
widgetRegistry.register(ImgWidget.KEY, ImgWidget);
|
widgetRegistry.register(ImgWidget.KEY, ImgWidget);
|
||||||
widgetRegistry.register(AddressWidget.KEY, AddressWidget);
|
widgetRegistry.register(AddressWidget.KEY, AddressWidget);
|
||||||
widgetRegistry.register(TinymceWidget.KEY, TinymceWidget);
|
widgetRegistry.register(TinymceWidget.KEY, TinymceWidget);
|
||||||
|
|||||||
2
src/typings.d.ts
vendored
2
src/typings.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
// # 3rd Party Library
|
// # 3rd Party Library
|
||||||
// If the library doesn't have typings available at `@types/`,
|
// If the library doesn't have typings available at `@types/`,
|
||||||
// you can still use it by manually adding typings for it
|
// you can still use it by manually adding typings for it
|
||||||
declare module 'quill-image-resize-module';
|
// declare module 'quill-image-resize-module';
|
||||||
|
|||||||
Reference in New Issue
Block a user