This commit is contained in:
Jesse-Ma
2022-07-29 15:21:20 +08:00
parent deb1fbe49d
commit 732907c3b6
22 changed files with 592 additions and 415 deletions

View File

@@ -62,12 +62,65 @@
.layout-footer-center {
display: none;
}
}
</style>
<style>
.ivu-btn-text:focus {
margin-top: -3px;
box-shadow: none !important;
}
.tab_pre {
display: inline;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px;
}
.tab_pre::selection {
background: #ed4014;
color: #FFFFFF;
}
.tab_pre::-moz-selection {
background: #ed4014;
color: #FFFFFF;
}
.vue-contextmenu-listWrapper {
background: #ed4014 !important;
border-radius: 0px !important;
}
.vue-contextmenu-listWrapper .context-menu-list {
background: #ed4014 !important;
margin: 0px !important;
}
.context-menu-list:hover {
background: #f16643 !important;
}
.btn-wrapper-simple {
height: 24px !important;
margin-top: 1px !important;
text-align: left !important;
}
.no-child-btn {
padding: 0px 10px !important;
}
.nav-name-right {
margin: 0px 20px 0px 5px !important;
color: #ffffff !important;
font-size: 14px !important;
line-height: 24px !important;
font-family: "Bitstream Vera Sans Mono", Consolas, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei";
}
@@ -78,103 +131,80 @@
button span {
font-size: 18px;
margin-left: -1px !important;
margin-bottom: 5px;
margin-bottom: 4px;
}
</style>
<template>
<div class="layout">
<Layout>
<Affix :offset-top="0">
<Header class="header">
<div>
<Row>
<Col :xs="{ span: 24, offset: 0 }" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }"
:lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
<div id="textToTop" v-on:click="toTop()" :class="[toTopState ? 'showBlock' : 'hideBlock']"
style="width: 100%;height: 20px;position: absolute;top: 35px;">
<div style="display:inline-block;cursor: pointer;padding:2px;">
<Icon type="ios-arrow-up" size="28" color="#ed4014" />
</div>
<Row>
<Col :xs="{ span: 24, offset: 0 }" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }"
:lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
<Affix :offset-top="0">
<div style="background: white;width:100%;height:40px;">
<img style="height:40px;float:left;" alt="refresh flagnote" src="/static/favicon.png">
<div style="float:left;width:auto;">
<Button-group size="large">
<Button aria-label="share" type="error"
style="margin-left:5px; border-radius: 0px;font-size: 24px; font-family: Arial, sans-serif"
@click="showShareModel()" icon="md-cloud-done">{{ noteForm.ttlDesc }}</Button>
<Button aria-label="download text" v-show="model.showDownloadText" type="error"
style="margin-left:5px; border-radius: 0px; font-size: 22px;" @click="downLoadText()"
icon="md-download"></Button>
</Button-group>
</div>
<div style="background: white;width:100%;height:40px;">
<img style="height:40px;float:left;" src="favicon.png">
<div style="float:left;width:auto;">
<Button-group size="large">
<div style="float:right;width:auto;">
<Button-group size="large">
<Button aria-label="to top" v-show="toTopState" type="text"
style="margin-left:0px; border-radius: 0px; font-size: 28px;color:red;line-height: 20px;"
@click="toTop()" icon="ios-arrow-up" ghost></Button>
<Button type="error"
style="margin-left:5px; border-radius: 0px;font-size: 24px; font-family: Arial, sans-serif"
@click="showShareModel()" icon="md-cloud-done">{{ noteForm.ttlDesc }}</Button>
<!--
<Button type="error"
style="margin-left:5px; border-radius: 0px; font-size: 22px;"
@click="downLoadImage()" icon="md-download">.png</Button>
-->
<Button v-show="model.showDownloadText" type="error"
style="margin-left:5px; border-radius: 0px; font-size: 22px;" @click="downLoadText()"
icon="md-download"></Button>
</Button-group>
</div>
<div style="float:right;width:auto;">
<Button-group size="large">
<Button type="error" style="margin-left:0px; border-radius: 0px;font-size: 24px;"
@click="createNote()" icon="md-add"></Button>
<Button type="error" style="margin-left:5px; border-radius: 0px;font-size: 24px;"
@click="showDeleteModel()" icon="md-trash"></Button>
</Button-group>
</div>
<!--
<div style="width:auto;float:right;font-size: 24px;">
<Button-group vertical size="large" style="position:absolute;right;0px;top:0px;">
<Button type="error" style="border-radius: 0px;font-size: 24px;" icon="md-add"
@click="showExt = true"></Button>
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
icon="md-cloud-upload"></Button>
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
icon="logo-googleplus"></Button>
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
icon="logo-tumblr"></Button>
</Button-group>
</div>
-->
<Button aria-label="create note" type="error"
style="margin-left:10px; border-radius: 0px;font-size: 24px;" @click="createNote()"
icon="md-add"></Button>
<Button aria-label="delete note" type="error"
style="margin-left:5px; border-radius: 0px;font-size: 24px;" @click="showDeleteModel()"
icon="md-trash"></Button>
</Button-group>
</div>
</div>
</Affix>
</Col>
</Row>
</div>
</Col>
</Row>
</Header>
</Affix>
<Content class="content">
<div style="min-height: 650px;">
<Row>
<Col :xs="{ span: 24, offset: 0 }" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }"
:lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
<Card :padding="0">
<div style="border-left: 0px solid #FF3366;">
<div id="wrapper" style="border-left: 0px solid #FF3366;" @contextmenu="showMenu">
<vue-context-menu :contextMenuData="contextMenuData" @selectAllText="selectAllText"
@copySelectedText="copySelectedText" @copyAllText="copyAllText">
</vue-context-menu>
<div id="noteText" style="text-align: left;min-height: 650px;" class="monoFt"
v-html="noteForm.escapeText"></div>
v-html="noteForm.escapeText">
</div>
</div>
</Card>
</Col>
@@ -185,7 +215,9 @@ button span {
<Footer class="layout-footer-center">2022 &copy; flagnote.com</Footer>
<Footer class="layout-footer-center">
2022 &copy; flagnote.com
</Footer>
</Layout>
@@ -202,10 +234,11 @@ button span {
<Modal v-model="model.showDelete" width="330" footer-hide class-name="fnmodal" :styles="{ borderRadius: 0 }">
<p style="text-align: center;font-size:medium;margin-bottom: 20px;">
{{$t("message.askTodelete")}}
{{ $t("message.askTodelete") }}
</p>
<p style="text-align: center;">
<Button type="error" :loading="model.delete" style="border-radius: 0px;" @click="dropNote()">{{$t("button.yes")}}</Button>
<Button type="error" :loading="model.delete" style="border-radius: 0px;" @click="dropNote()">{{ $t("button.yes")
}}</Button>
</p>
</Modal>
@@ -220,14 +253,13 @@ button span {
import { aesDecrypt, md5, unzip } from "@/libs/secret";
import Jquery from "jquery";
import { getSecretKey, getStoreKey } from "@/api/lock";
import { deleteNote } from "@/api/note";
import { deleteNote, getNote } from "@/api/note";
import storage from "@/libs/storage";
import { getEscapeText } from "@/libs/noteStorage";
import QRCode from "qrcode";
import Clipboard from "clipboard";
import { saveAs } from 'file-saver';
import { isWeixin } from "@/libs/utils";
// import html2canvas from 'html2canvas';
import { isWeixin, getNoteUrl } from "@/libs/utils";
export default {
name: 'ViewNote',
@@ -244,7 +276,7 @@ export default {
md5: '',
lock: 0,
ttl: 3600,
ttlDesc: '',
ttlDesc: '-- : --',
},
secret: {
storeKey: '',
@@ -263,24 +295,48 @@ export default {
showDelete: false,
showShare: false,
deleting: false,
showDownloadText: true,
showDownloadText: false,
},
toTopState: false,
contextMenuData: {
menuName: 'textMenu',
//菜单显示的位置
axis: {
x: null,
y: null
},
//菜单选项
menulists: [{
fnHandler: 'selectAllText', //绑定事件
btnName: this.$t("button.selectAll") //菜单名称
}, {
fnHandler: 'copySelectedText',
btnName: this.$t("button.copy")
}, {
fnHandler: 'copyAllText',
btnName: this.$t("button.copyAll")
}]
},
tempFragment: null,
}
},
created() {
// read $route
this.noteForm.key = this.$route.params.name;
let noteMeta = this.$route.meta.noteMeta;
//ipad chrome url not redirect
let path = location.pathname;
if ("/" == path) {
history.pushState('', '', '/' + this.noteForm.key);
}
//wx does not show downloadText
this.model.showDownloadText = !isWeixin();
let noteMeta = this.$route.meta.noteMeta;
this.noteForm.key = this.$route.params.name;
this.noteForm.noteUrl = "https://flagnote.com/" + this.noteForm.key;
let storeKey = getStoreKey(this.noteForm.key);
this.secret.storeKey = storeKey;
this.noteForm.noteUrl = getNoteUrl(this.noteForm.key);
this.secret.storeKey = getStoreKey(this.noteForm.key);
if (noteMeta) {
this.state.lock = noteMeta.lock;
@@ -292,79 +348,79 @@ export default {
this.noteForm.ttl = noteMeta.ttl;
this.startClock();
storage.local.dynamicClear();
this.loadText();
storage.local.dynamicClear(this.secret.currentTime);
this.bindCtrlAllEvent();
this.bindCopyUrlEvent();
this.bindToTopEvent();
this.bindMouseEvent();
} else {
alert("Unconnected.");
}
this.bindEvent();
var clipboard = new Clipboard("#tag-copy")
let that = this;
clipboard.on('success', function () {
//console.log("url copied")
//that.$Message.success({content:'url copied',duration: 10,closable:true});
that.$Message.success({ content: 'url copied' });
});
clipboard.on('error', function () {
that.$Message.error('not allow to copy');
});
window.onscroll = function () {
//变量scrollTop是滚动条滚动时距离顶部的距离
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// //变量windowHeight是可视区的高度
// var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
// //变量scrollHeight是滚动条的总高度
// var scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
//滚动条到底部的条件
if (scrollTop >= 20) {
//写后台加载数据的函数
//console.log("距顶部" + scrollTop + "可视区高度" + windowHeight + "滚动条总高度" + scrollHeight);
that.toTopState = true;
} else {
//toTop.style.setProperty("display", "none")
that.toTopState = false;
}
}
},
methods: {
mounted() {
this.bindCopyTextEvent();
const myObserver = new ResizeObserver(entries => {
// iterate over the entries, do something.
entries.forEach(entry => {
let affix = document.querySelector('.ivu-affix');
if (affix) {
affix.setAttribute("style", "top: 0px; width: " + entry.contentRect.width + "px;");
}
});
});
const someOtherEl = document.querySelector('#wrapper');
myObserver.observe(someOtherEl);
},
updated() {
},
beforeDestroy() {
},
destroyed() {
},
computed: {},
watch: {},
methods: {
selectAllText() {
var element = document.getElementById("noteText");
if (window.getSelection) {
let selection = window.getSelection();
let range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
},
copySelectedText() {
},
copyAllText() {
},
showMenu(event) {
event.preventDefault()
var x = event.clientX
var y = event.clientY
// Get the current location
this.contextMenuData.axis = {
x, y
}
},
downLoadText() {
var blob = new Blob([this.noteForm.text], { type: "application/octet-stream;charset=utf-8" });
saveAs(blob, this.noteForm.key + ".txt");
},
downLoadImage() {
//let that = this;
//html2canvas(document.getElementById("noteText")).then(function(canvas) {
// var canvas = document.getElementById("my-canvas");
//canvas.toBlob(function(blob) {
// saveAs(blob, that.noteForm.key+".png");
// });
// alert(canvas)
// document.body.appendChild(canvas);
//});
},
toTop() {
window.scrollTo(0, 0);
this.downLoadImage();
},
startClock() {
let that = this;
@@ -403,7 +459,6 @@ export default {
window.open("/");
},
showShareModel() {
this.model.showShare = true;
let qrimg = document.getElementById("qrimg");
let qrurl = "https://flagnote.com/" + this.noteForm.key;
@@ -423,7 +478,6 @@ export default {
},
showDeleteModel() {
this.model.showDelete = true;
},
dropNote() {
this.model.deleting = true;
@@ -431,7 +485,6 @@ export default {
deleteNote(this.noteForm.key).then(res => {
if (res) {
storage.local.delete(that.secret.storeKey + '.text');
storage.session.delete(that.secret.storeKey + '.keyMeta');
location.reload();
} else {
that.model.deleting = false;
@@ -439,25 +492,28 @@ export default {
});
},
loadText() {
let password;
let password = '';
if (this.noteForm.lock == 1) {
password = "FLAGNOTE"; //默认密码
}
if (!password) {
password = "";
}
let secretKey = getSecretKey(this.noteForm.key, password);
let storeText = storage.local.getText(this.secret.storeKey + '.text');
if (!storeText || (md5(storeText.substring(51)) != this.noteForm.md5)) {
let note = this.getNote(this.noteForm.key);
storeText = this.noteForm.lock + '|' + this.secret.cipher + '|1|' + this.noteForm.currentTime + '|' + note.text;
storage.local.setText(this.secret.storeKey + '.text', storeText);
// local is useless
let note = getNote(this.noteForm.key);
// if lack of local , not set local
if (storage.local.getAvailableSize() > 1 * 1024 * 1024) {
storeText = this.noteForm.lock + '|' + this.secret.cipher + '|1|' + this.secret.currentTime + '|' + note.text;
storage.local.setText(this.secret.storeKey + '.text', storeText);
}
} else {
// local is usable, and set commited flag
var starray = storeText.split('|');
storage.local.setText(this.secret.storeKey + '.text', starray[0] + "|" + starray[1] + "|1|" + starray[3] + "|" + starray[4]);
if ("0" == starray[2]) {
storage.local.setText(this.secret.storeKey + '.text', starray[0] + "|" + starray[1] + "|1|" + starray[3] + "|" + starray[4]);
}
}
if (storeText) {
@@ -465,36 +521,84 @@ export default {
let plainText = aesDecrypt(storeText, secretKey);
if (plainText.startsWith("FLAGNOTE#")) {
this.noteForm.text = plainText.substring(9);
let escapeText = getEscapeText(this.noteForm.text);
this.noteForm.escapeText = escapeText;
this.noteForm.escapeText = getEscapeText(this.noteForm.text);
}
}
},
getNote(key) {
let noteObject;
Jquery.ajax({
url: '/note/' + key,
async: false,
success: function (data) {
noteObject = data;
bindToTopEvent() {
let that = this;
window.onscroll = function () {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollTop >= 20) {
that.toTopState = true;
} else {
that.toTopState = false;
}
}
},
bindCopyUrlEvent() {
let that = this;
var clipboard = new Clipboard("#tag-copy")
clipboard.on('success', function () {
that.$Message.success({ content: 'url copied' });
});
clipboard.on('error', function () {
that.$Message.error('not allow to copy');
});
},
bindCopyTextEvent() {
let ele1 = document.getElementsByClassName("vue-contextmenuName-textMenu")[0].children[1].children[0].children[0];
let ele2 = document.getElementsByClassName("vue-contextmenuName-textMenu")[0].children[2].children[0].children[0];
let that = this;
const clipboard1 = new Clipboard(ele1, { // 绑定需要的触发的dom
text: function () {
let fragment = that.tempFragment;
if (fragment) {
let copyText = "";
fragment.childNodes.forEach(element => {
if (3 == element.nodeType) {
copyText += element.textContent.replace(new RegExp('\u00a0', 'gm'), " ");
} else if (1 == element.nodeType) {
if ("BR" == element.nodeName) {
copyText += "\r\n";
} else if ("PRE" == element.nodeName) {
copyText += "\t";
}
}
});
return copyText;
}
return null;
}
});
return noteObject;
clipboard1.on('success', function () {
that.$Message.success({ content: 'selected text copied' });
});
clipboard1.on('error', function () {
that.$Message.error('not allow to copy');
});
const clipboard2 = new Clipboard(ele2, { // 绑定需要的触发的dom
text: function () {
return that.noteForm.text;
}
});
clipboard2.on('success', function () {
that.$Message.success({ content: 'text copied' });
});
clipboard2.on('error', function () {
that.$Message.error('not allow to copy');
});
},
bindEvent() {
if (document.body.createTextRange) {
Jquery(document).keydown(function (e) {
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {
e.preventDefault();
var element = document.getElementById("noteText");
let range = document.body.createTextRange();
range.moveToElementText(element);
range.select();
}
});
} else if (window.getSelection) {
bindCtrlAllEvent() {
if (window.getSelection) {
Jquery(document).keydown(function (e) {
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {
@@ -511,11 +615,20 @@ export default {
}
});
} else {
//alert('none');
}
},
bindMouseEvent() {
let that = this;
document.addEventListener('mouseup', function (e) {
if (e.button === 2) {
if (window.getSelection) {
let sel = window.getSelection();
if (sel.rangeCount > 0) {
that.tempFragment = sel.getRangeAt(0).cloneContents();
}
}
}
}, false)
}
}