161 lines
5.5 KiB
HTML
161 lines
5.5 KiB
HTML
<html>
|
|
<head>
|
|
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css" rel="stylesheet">
|
|
<link href="http://antifavicon.com/ex8.png" rel="icon">
|
|
<title>编辑邮件</title>
|
|
<style>
|
|
body {
|
|
height: 85vh;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 8px;
|
|
max-height: fit-content;
|
|
}
|
|
#subject {
|
|
border: 0px;
|
|
border-left: 1px solid #CCC;
|
|
border-right: 1px solid #CCC;
|
|
padding: 10.5px 16px 10.5px 16px;
|
|
outline: none;
|
|
width: 100%;
|
|
font-size: 14px;
|
|
line-height: calc(1.5 / 1);
|
|
}
|
|
input:-webkit-autofill {
|
|
-webkit-box-shadow: 0 0 0px 1000px white inset;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="toolbar">
|
|
<span class="ql-formats">
|
|
<!-- Add font size dropdown -->
|
|
<select class="ql-font">
|
|
</select>
|
|
<select class="ql-header">
|
|
<option value="1"></option>
|
|
<option value="2"></option>
|
|
<option value="3"></option>
|
|
<option value="4"></option>
|
|
<!-- Note a missing, thus falsy value, is used to reset to default -->
|
|
<option selected></option>
|
|
</select>
|
|
</span>
|
|
<span class="ql-formats">
|
|
<button class="ql-bold"></button>
|
|
<button class="ql-italic"></button>
|
|
<button class="ql-underline"></button>
|
|
<button class="ql-strike"></button>
|
|
</span>
|
|
<span class="ql-formats">
|
|
<button class="ql-script" value="sub"></button>
|
|
<button class="ql-script" value="super"></button>
|
|
<button class="ql-list" value="ordered"></button>
|
|
<button class="ql-list" value="bullet"></button>
|
|
</span>
|
|
<span class="ql-formats">
|
|
<button id="load">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="oklch(0.269 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
<path d="M14 3v4a1 1 0 0 0 1 1h4" />
|
|
<path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z" />
|
|
<path d="M12 11l0 6" />
|
|
<path d="M9 14l6 0" />
|
|
</svg>
|
|
</button>
|
|
<button id="save">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="oklch(0.269 0 0)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
<path d="M6 4h10l4 4v10a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2" />
|
|
<path d="M12 14m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
|
|
<path d="M14 4l0 4l-6 0l0 -4" />
|
|
</svg>
|
|
</button>
|
|
</span>
|
|
</div>
|
|
<input id="subject" name="subject" type="text" />
|
|
<div id="editor"></div>
|
|
</body>
|
|
<script>
|
|
const quill = new Quill('#editor', {
|
|
theme: 'snow',
|
|
modules: {
|
|
toolbar: '#toolbar'
|
|
}
|
|
});
|
|
|
|
const loadButton = document.querySelector("#load");
|
|
const saveButton = document.querySelector('#save');
|
|
const editor = document.querySelector('#editor');
|
|
const input = document.querySelector('#subject');
|
|
|
|
let reader = new FileReader();
|
|
let parser = new DOMParser();
|
|
let fileHandle = 'desktop';
|
|
|
|
reader.addEventListener('load', () => {
|
|
const contents = reader.result;
|
|
const virtual = parser.parseFromString(contents, 'text/html');
|
|
|
|
const body = virtual.querySelector('body');
|
|
const html = body ? body.innerHTML : contents;
|
|
|
|
const delta = quill.clipboard.convert({ html });
|
|
quill.setContents(delta, 'silent');
|
|
});
|
|
|
|
loadButton.addEventListener('click', async () => {
|
|
[fileHandle] = await window.showOpenFilePicker({
|
|
startIn: fileHandle,
|
|
types: [
|
|
{ accept: { "text/html": [".html"] }},
|
|
],
|
|
});
|
|
const file = await fileHandle.getFile();
|
|
const name = fileHandle.name;
|
|
const subject = name.substring(0, name.lastIndexOf('.'));
|
|
|
|
input.value = subject;
|
|
reader.readAsText(file);
|
|
});
|
|
|
|
saveButton.addEventListener('click', async () => {
|
|
fileHandle = await window.showSaveFilePicker({
|
|
startIn: fileHandle,
|
|
suggestedName: input.value,
|
|
types: [
|
|
{ accept: { "text/html": [".html"] }},
|
|
],
|
|
});
|
|
let writable = await fileHandle.createWritable();
|
|
let contents = editor.firstChild.innerHTML;
|
|
|
|
const name = fileHandle.name;
|
|
const subject = name.substring(0, name.lastIndexOf('.'));
|
|
input.value = subject;
|
|
|
|
let html = document.createElement('html');
|
|
let body = document.createElement('body');
|
|
let meta = document.createElement('meta');
|
|
let link = document.createElement('link');
|
|
|
|
if (!(/<html>[\s\S]*<\/html>/i).test(contents)) {
|
|
meta.setAttribute('charset', "UTF-8");
|
|
link.setAttribute('href', "https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css");
|
|
link.setAttribute('rel', "stylesheet");
|
|
body.setAttribute('class', "ql-container ql-editor ql-snow");
|
|
body.innerHTML = contents;
|
|
html.appendChild(meta);
|
|
html.appendChild(link);
|
|
html.appendChild(body);
|
|
contents = html.outerHTML;
|
|
}
|
|
|
|
await writable.write(contents);
|
|
await writable.close();
|
|
});
|
|
</script>
|
|
</html> |