Add files via upload

This commit is contained in:
Jesse-Ma
2022-11-22 14:58:24 +08:00
committed by GitHub
parent 732907c3b6
commit 79c1831e6f
19 changed files with 638 additions and 796 deletions

View File

@@ -5,6 +5,8 @@
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"build.test": "vue-cli-service build --mode test",
"build.production": "vue-cli-service build --mode production",
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
@@ -15,7 +17,6 @@
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"jquery": "^3.6.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pako": "^2.0.4", "pako": "^2.0.4",
"qrcode": "^1.5.0", "qrcode": "^1.5.0",

View File

@@ -2,12 +2,24 @@
<html lang=""> <html lang="">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" /> <meta name="renderer" content="webkit" />
<meta http-equiv="Expires" content="0"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta http-equiv="Pragma" content="no-cache"> <meta
<meta http-equiv="Cache-control" content="no-cache"> name="viewport"
<meta http-equiv="Cache" content="no-cache"> content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=2.0, user-scalable=no"
/>
<meta name="keywords" content="flagnote" />
<meta name="description" content="flag note" />
<meta name="theme-color" content="#ed4014" />
<meta name="format-detection" content="telephone=no,email=no,adress=no" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-control" content="no-cache" />
<meta http-equiv="Cache" content="no-cache" />
<link rel="icon" href="/static/favicon.ico" /> <link rel="icon" href="/static/favicon.ico" />
<title>flagnote.com</title> <title>flagnote.com</title>
<style> <style>

View File

@@ -2,13 +2,25 @@
<html lang=""> <html lang="">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" /> <meta name="renderer" content="webkit" />
<meta name="theme-color" content="#ed4014"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta http-equiv="Expires" content="0"> <meta
<meta http-equiv="Pragma" content="no-cache"> name="viewport"
<meta http-equiv="Cache-control" content="no-cache"> content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=2.0, user-scalable=no"
<meta http-equiv="Cache" content="no-cache"> />
<meta name="keywords" content="flagnote" />
<meta name="description" content="flag note" />
<meta name="theme-color" content="#ed4014" />
<meta name="format-detection" content="telephone=no,email=no,adress=no" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-control" content="no-cache" />
<meta http-equiv="Cache" content="no-cache" />
<link rel="icon" href="/static/favicon.ico" /> <link rel="icon" href="/static/favicon.ico" />
<title>flagnote.com</title> <title>flagnote.com</title>
<style> <style>
@@ -83,8 +95,7 @@
<strong <strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to properly without JavaScript enabled. Please enable it to
continue.</strong continue.</strong>
>
</noscript> </noscript>
<div id="app"></div> <div id="app"></div>
<!-- built files will be auto injected --> <!-- built files will be auto injected -->

View File

@@ -13,7 +13,6 @@ export default {
</script> </script>
<style> <style>
.ivu-btn:focus { .ivu-btn:focus {
box-shadow: none !important; box-shadow: none !important;
} }
@@ -28,8 +27,8 @@ export default {
body { body {
font-family: "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, font-family: "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial,
"PingFang SC", "Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei", "PingFang SC", "Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei",
sans-serif; sans-serif;
background-color: #dddddd; background-color: #dddddd;
} }
@@ -42,15 +41,18 @@ body {
} }
.showBlock{ .showBlock {
display: block; display: block;
} }
.hideBlock{ .hideBlock {
display: none; display: none;
} }
#noteText { #noteText {
white-space: pre-wrap;
word-break: break-word;
word-wrap: break-word;
color: black; color: black;
padding: 10px; padding: 10px;
vertical-align: top; vertical-align: top;
@@ -101,18 +103,11 @@ body {
font-family: "Bitstream Vera Sans Mono", Consolas, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei" font-family: "Bitstream Vera Sans Mono", Consolas, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei"
} }
.showBlock{ .showBlock {
display: block; display: block;
} }
.hideBlock{ .hideBlock {
display: none; display: none;
} }
</style> </style>

View File

@@ -1,40 +1,13 @@
// import Jquery from "jquery"; import { aesEncrypt, md5 } from "@/libs/secret";
import {aesEncrypt, md5} from "@/libs/secret";
// export function getSecretKey(key, password) {
// console.log("getSecretKey");
// let secretKey = '';
// Jquery.ajax({
// method: 'POST',
// url: '/note/' + key + "/secretKey",
// async: false,
// contentType: 'application/json',
// dataTeyp: 'json',
// data: JSON.stringify({'password': password}),
// success: function (data) {
// secretKey = data;
// },
// error: function () {
// alert('服务器链接异常 ');
// },
// });
// return secretKey;
// }
export function getStoreKey(key) { export function getStoreKey(key) {
return md5(aesEncrypt(key, key)); return md5(key + key);
} }
export function getSecretKey(cipher, password) { export function getSecretKey(key, password) {
if(!password){ if (!password) {
password = ''; password = key;
} }
return md5(cipher + password);
return md5(aesEncrypt(key, password));
} }

View File

@@ -1,109 +1,99 @@
import axios from "axios"; import axios from "axios";
import Jquery from "jquery"; import { getStoreKey } from "@/api/lock";
import {getStoreKey} from "@/api/lock";
import storage from "@/libs/storage"; import storage from "@/libs/storage";
import { md5 } from "@/libs/secret";
const servicePath = 'https://service.flagnote.com'; const servicePath = "https://flagnote.com";
//const servicePath = "http://localhost:8080";
export function saveNote(noteForm) { export function saveNote(noteForm, secret) {
let storeKey = getStoreKey(noteForm.key); let storeKey = secret.storeKey;
let storeText = storage.local.getText(storeKey + '.text');
let note = {
"lock": storeText.substring(0, 1),
"cipher": storeText.substring(2, 34),
"text": storeText.substring(35+16),
"key": noteForm.key
}
return axios({ let storeInfo = storage.local.getText(storeKey);
url: servicePath+'/note/' + noteForm.key, let starray = storeInfo.split("|");
method: 'post',
data: note let note = {
} lock: starray[0],
) cipher: starray[1],
text: starray[4],
key: noteForm.key,
};
let bufferArrary = eval("[" + note.text + "]");
let array = Uint8Array.from(bufferArrary);
let blob = new Blob([array], { type: "application/octet-stream" });
let form = new FormData();
form.append("file", blob, noteForm.key);
form.append("cipher", note.cipher);
form.append("lock", note.lock);
form.append("key", note.key);
form.append("md5", md5(note.text));
let config = {
headers: { "Content-Type": "multipart/form-data" },
};
return axios({
url: servicePath + "/note/" + noteForm.key,
method: "post",
data: form,
config: config,
});
} }
export function deleteNote(key) { export function deleteNote(key) {
let storeKey = getStoreKey(key); let storeKey = getStoreKey(key);
let storeText = storage.local.getText(storeKey + '.text'); let storeInfo = storage.local.getText(storeKey);
let note = { let note = {
"cipher": storeText.substring(2, 34), cipher: storeInfo.substring(2, 34),
"key": key key: key,
} };
return axios({ return axios({
url: servicePath+'/note/' + key +'/delete', url: servicePath + "/note/" + key + "/delete",
method: 'post', method: "post",
data: note data: note,
} });
)
} }
// export function getSecretKey(key, password) { export function getNoteBlob(key) {
// console.log("getSecretKey"); return axios({
// let secretKey = ''; url: servicePath + "/note/" + key,
// Jquery.ajax({ method: "get",
// method: 'POST', responseType: "blob",
// url: '/note/' + key + "/secretKey", ignoreError: 1,
// async: false, original: true,
// contentType: 'application/json', source: true,
// dataTeyp: 'json', });
// data: JSON.stringify({'password': password}), }
// success: function (data) {
// secretKey = data;
// },
// error: function () {
// alert('服务器链接异常 ');
// },
// });
// return secretKey;
// }
export function getNoteMeta(key) { export function getNoteMeta(key) {
let noteMeta = {}; let url = servicePath + "/note/" + key + "/noteMeta";
Jquery.ajax({ let noteMeta = ajaxGet(url);
method: 'GET', return noteMeta;
url: servicePath+'/note/' + key + "/noteMeta",
async: false,
success: function (data) {
noteMeta = eval(data); //eval("(" + data + ")");
},
error: function () {
noteMeta = null;
},
});
return noteMeta;
} }
export function getNote(key) {
let noteObject = {};
Jquery.ajax({
method: 'GET',
url: servicePath + '/note/' + key,
async: false,
success: function (data) {
noteObject = eval(data);
},
error: function () {
noteObject = null;
},
});
return noteObject;
}
export function getKeyMeta() { export function getKeyMeta() {
let keyMeta = {}; let url = servicePath + "/note/keyMeta";
Jquery.ajax({ let keyMeta = ajaxGet(url);
method: 'GET', return keyMeta;
url: servicePath+'/note/keyMeta', }
async: false,
success: function (data) { export function ajaxGet(url) {
keyMeta = eval(data); //eval("(" + data + ")"); let data = {};
}, let xmlhttp = new XMLHttpRequest();
error: function () { xmlhttp.open("GET", url, false);
keyMeta = null; xmlhttp.onreadystatechange = () => {
}, if (xmlhttp.readyState == 4) {
}); if (xmlhttp.status == 200 || xmlhttp.status == 304) {
return keyMeta; if(xmlhttp.responseText){
data = JSON.parse(xmlhttp.responseText);
}else{
data ={};
}
}
}
};
xmlhttp.send();
return data;
} }

View File

@@ -3,7 +3,7 @@ const zh = {
askTodelete: "是否确定要删除?", askTodelete: "是否确定要删除?",
}, },
content: { content: {
blankTip: "我是布兰克。", blankTip: "I am Blank.",
}, },
button: { button: {
ok: "好的", ok: "好的",

View File

@@ -1,49 +1,45 @@
import {zip, aesEncrypt} from '../libs/secret' import { wrap } from "@/libs/secret";
import storage from "@/libs/storage"; import storage from "@/libs/storage";
import {getSecretKey} from "@/api/lock";
import escapeHtml from "escape-html"; import escapeHtml from "escape-html";
export function setStoreText(noteForm, secret, password) { export function setStoreText(noteForm, state, secret) {
let text = noteForm.text; let text = noteForm.text;
if (!text) { let storeText = "";
return; if (text) {
} storeText = wrap(text, secret.secretKey);
}
if (!password) { storage.local.setText(
password = ""; secret.storeKey,
} state.lock +
"|" +
text = "FLAGNOTE#" + text; secret.cipher +
"|" +
let secretKey = getSecretKey(noteForm.key, password); state.commited +
let storeText = aesEncrypt(text, secretKey); "|" +
storeText = zip(storeText); state.initTime +
"|" +
let lock = 0; storeText
if (password) { );
lock = 1;
}
storage.local.setText(secret.storeKey + '.text', lock + '|' + secret.cipher + '|0|'+ noteForm.initTime+'|' + storeText);
} }
export function clearStoreText(key) { export function clearStoreText(key) {
if (!key) { if (!key) {
return; return;
} }
storage.local.delete(key); storage.local.delete(key);
} }
export function getEscapeText(text) { export function getEscapeText(text) {
let textEscape = escapeHtml(text); let textEscape = escapeHtml(text);
textEscape = textEscape.replace(new RegExp(' ','gm'), "&nbsp;"); textEscape = textEscape.replace(new RegExp(" ", "gm"), "&nbsp;");
textEscape = textEscape.replace(new RegExp('\\r\\n','gm'), "<br/>"); textEscape = textEscape.replace(new RegExp("\\r\\n", "gm"), "<br/>");
textEscape = textEscape.replace(new RegExp('\\r','gm'), "<br/>"); textEscape = textEscape.replace(new RegExp("\\r", "gm"), "<br/>");
textEscape = textEscape.replace(new RegExp('\\n','gm'), "<br/>"); textEscape = textEscape.replace(new RegExp("\\n", "gm"), "<br/>");
textEscape = textEscape.replace(new RegExp('\\t','gm'), "<pre class=\"tab_pre\">&#9;</pre>"); textEscape = textEscape.replace(
return textEscape; new RegExp("\\t", "gm"),
'<pre class="tab_pre">&#9;</pre>'
);
return textEscape;
} }

View File

@@ -1,57 +1,77 @@
import CryptoJS from 'crypto-js' import CryptoJS from "crypto-js";
import pako from 'pako' import pako from "pako";
/** /**
* @word 要加密的内容 * @word 要加密的内容
* @keyWord String 服务器随机返回的关键字 * @keyWord String 服务器随机返回的关键字
* */ * */
export function wrap(text, secretKey) {
text = "FLAGNOTE#" + text;
let result = aesEncrypt(text, secretKey);
result = zip(result);
return result;
}
export function unwrap(storeText, secretKey) {
let result = unzip(storeText);
result = aesDecrypt(result, secretKey);
if (result.startsWith("FLAGNOTE#")) {
return result.substring(9);
}
return null;
}
//加密 //加密
export function md5(word, keyWord = 'F1agn0te') { export function md5(word, keyWord = "F1agn0te") {
let srcWords = CryptoJS.enc.Utf8.parse(word + '_' + keyWord); let srcWords = CryptoJS.enc.Utf8.parse(word + "_" + keyWord);
let encrypted = CryptoJS.MD5(srcWords); let encrypted = CryptoJS.MD5(srcWords);
return encrypted.toString(); return encrypted.toString();
} }
//加密 //加密
export function aesEncrypt(word, keyWord) { export function aesEncrypt(word, keyWord) {
let key = CryptoJS.enc.Utf8.parse(keyWord); let key = CryptoJS.enc.Utf8.parse(keyWord);
let srcWords = CryptoJS.enc.Utf8.parse(word); let srcWords = CryptoJS.enc.Utf8.parse(word);
let encrypted = CryptoJS.AES.encrypt(srcWords, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}); let encrypted = CryptoJS.AES.encrypt(srcWords, key, {
return encrypted.toString(); mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return encrypted.toString();
} }
//解密 //解密
export function aesDecrypt(word, keyWord) { export function aesDecrypt(word, keyWord) {
let key = CryptoJS.enc.Utf8.parse(keyWord); let key = CryptoJS.enc.Utf8.parse(keyWord);
let decrypt = CryptoJS.AES.decrypt(word, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}); let decrypt = CryptoJS.AES.decrypt(word, key, {
return CryptoJS.enc.Utf8.stringify(decrypt).toString() mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
} }
export function encode(text) { export function encode(text) {
return btoa(encodeURIComponent(text)) return btoa(encodeURIComponent(text));
} }
export function decode(text) { export function decode(text) {
return decodeURIComponent(atob(text)) return decodeURIComponent(atob(text));
} }
export function unzip(text) { export function unzip(text) {
let charData = text.split(',').map(function (x) { let charData = text.split(",").map(function (x) {
return parseInt(x) return parseInt(x);
}); });
let binData = new Uint8Array(charData); let binData = new Uint8Array(charData);
let data = pako.ungzip(binData); let data = pako.ungzip(binData);
//text = String.fromCharCode.apply(null, new Uint8Array(data)); //text = String.fromCharCode.apply(null, new Uint8Array(data));
text = new Uint8Array(data).reduce(function (data, byte) { text = new Uint8Array(data).reduce(function (data, byte) {
return data + String.fromCharCode(byte); return data + String.fromCharCode(byte);
}, ''); }, "");
return text; return text;
} }
export function zip(text) { export function zip(text) {
text = pako.gzip(text, {to: 'string'}); text = pako.gzip(text, { to: "string" });
return text; return text;
} }

View File

@@ -13,11 +13,14 @@ Vue.use(VueContextMenu)
axios.defaults.baseURL="https://service.flagnote.com" axios.defaults.baseURL="https://flagnote.com";
Vue.use(VueAxios, axios) Vue.use(VueAxios, axios)
Vue.config.productionTip = false const debugFlag = process.env.NODE_ENV !== 'production';
Vue.config.debug = debugFlag;
Vue.config.devtools = debugFlag;
Vue.config.productionTip = debugFlag;
new Vue({ new Vue({
i18n, i18n,

View File

@@ -46,13 +46,13 @@ function getNoteView() {
let storeKey = getStoreKey(key); let storeKey = getStoreKey(key);
let storeText = storage.local.getText(storeKey + ".text"); let storeInfo = storage.local.getText(storeKey);
if (storeText) { if (storeInfo) {
let starray = storeText.split("|"); let starray = storeInfo.split("|");
let commitFlag = starray[2]; let commitFlag = starray[2];
if (commitFlag == "1") { if (commitFlag == "1") {
//timeout and clear local //timeout and clear local
storage.local.delete(storeKey + '.text'); storage.local.delete(storeKey );
return BlankNote; return BlankNote;
} else { } else {
//secondEdit //secondEdit

View File

@@ -34,24 +34,22 @@
border: 0px solid #dcdee2; border: 0px solid #dcdee2;
border-color: #e8eaec; border-color: #e8eaec;
} }
</style> </style>
<style> <style>
</style> </style>
<template> <template>
<div class="layout" onkeydown="keydown"> <div class="layout" onkeydown="keydown">
<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 }">
<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 }">
<Affix :offset-top="0">
<div style="background: white;width:100%;height:40px;"> <div style="background: white;width:100%;height:40px;">
<img style="height:40px;float:left;" src="/static/favicon.png"> <img style="height:40px;float:left;" src="/static/favicon.png">
@@ -71,20 +69,22 @@
</Col> </Affix>
</Row> </Col>
</div> </Row>
</Header> </div>
</Affix> </Header>
<Content class="content"> <Content class="content">
<div style="min-height: 650px;"> <div style="min-height: 650px;">
<Row> <Row>
<Col :xs="{ span: 24, offset: 0 }" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }" <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 }"> :lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
<Card :padding="0"> <Card :padding="0">
<div style="border-left: 0px solid #FF3366;"> <div id="wrapper" style="border-left: 0px solid #FF3366;">
<div id="noteText" style="text-align: center;min-height: 650px;" class="monoFt"> <div id="noteText" style="text-align: center;min-height: 650px;" class="monoFt">
<h1>{{$t("content.blankTip")}}</h1> <h1></h1>
{{ $t("content.blankTip") }}
</div> </div>
</div> </div>
</Card> </Card>
@@ -132,7 +132,28 @@ export default {
created() { created() {
this.noteForm.key = this.$route.params.name; this.noteForm.key = this.$route.params.name;
}, },
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);
},
methods: { methods: {
refreshPage() {
window.location.reload();
},
createNote() { createNote() {
window.open("/"); window.open("/");
} }

View File

@@ -63,10 +63,10 @@
</style> </style>
<style> <style>
.ivu-btn-text:focus { /* .ivu-btn-text:focus {
margin-top: -3px; margin-top: -3px;
box-shadow: none !important; box-shadow: none !important;
} } */
</style> </style>
@@ -83,20 +83,19 @@
<Affix :offset-top="0"> <Affix :offset-top="0">
<div style="background: white;width:100%;height:40px;"> <div style="background: white;width:100%;height:40px;">
<img style="height:40px;float:left;" alt="refresh flagnote" src="/static/favicon.png"> <img style="height:40px;float:left;cursor: pointer;" alt="refresh flagnote" src="/static/favicon.png"
v-on:click="refreshPage()" />
<div style="float:left;width:auto;"> <div style="float:left;width:auto;">
<Button-group size="large"> <Button-group size="large">
<Button aria-label="publish" type="error" :loading="loading"
style="margin-left:5px; border-radius: 0px;font-size: 24px;" @click="submitNote()"
icon="md-cloud-upload"></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 aria-label="to top" v-show="toTopState" type="text"
style="margin-left:5px; border-radius: 0px; font-size: 28px;color:red;line-height: 20px;"
@click="toTop()" icon="ios-arrow-up" ghost></Button>
</Button-group> </Button-group>
</div> </div>
@@ -106,22 +105,38 @@
<div style="float:right;width:auto;"> <div style="float:right;width:auto;">
<Button-group size="large"> <Button-group size="large">
<Button aria-label="publish" type="error" :loading="model.submitting"
<Button aria-label="to top" v-show="toTopState" type="text" style="margin-left:5px; border-radius: 0px;font-size: 24px;" @click="submitNote()"
style="margin-left:0px; border-radius: 0px; font-size: 28px;color:red;line-height: 20px;" icon="md-cloud-upload"></Button>
@click="toTop()" icon="ios-arrow-up" ghost></Button> <Button aria-label="menu" type="error" style="margin-left:5px; border-radius: 0px;font-size: 24px;"
@click="switchMenu()" @blur.native="hideMenu()" icon="md-menu"></Button>
<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> </Button-group>
</div> </div>
</div> </div>
<div id="noteMenu" :class="[showMenuState ? 'showBlock' : 'hideBlock']"
style="z-index: 100; position: absolute;top:41px;right:0px;left:auto;">
<Button-group vertical size="large">
<Button aria-label="create note" type="error" style="border-radius: 0px;font-size: 24px;"
@click="createNote(); switchMenu(); " icon="md-add"></Button>
<!--
<Button type="error" icon="md-refresh" style="border-radius: 0px;font-size: 14px;font-size: 24px;"
@click="refreshPage()"></Button>
-->
<Button aria-label="download text" v-show="model.showDownloadText" type="error"
style="border-radius: 0px;font-size: 24px;" @click="downLoadText(); switchMenu(); "
icon="md-download"></Button>
<Button type="error" icon="md-trash" style="border-radius: 0px;font-size: 24px;"
@click="showDeleteModel(); switchMenu(); "></Button>
</Button-group>
</div>
</Affix> </Affix>
@@ -138,9 +153,9 @@
<Card :padding="0"> <Card :padding="0">
<Form :model="noteForm" :label-width="80"> <Form :model="noteForm" :label-width="80">
<div id="wrapper" style="border-left: 0px solid #FF3366;"> <div id="wrapper" style="border-left: 0px solid #FF3366;">
<Input element-id="noteText" type="textarea" :border="false" v-model="noteForm.text" <Input element-id="noteText" type="textarea" :border="false" v-model="noteForm.text" autofocus
:autosize="{ minRows: 30, maxRows: 20480 }" placeholder="Enter something..." v-on:input="log" :autosize="{ minRows: 30, maxRows: 20480 }" placeholder="Enter something..." @input="recordText"
@on-keydown="down" /> @on-keydown="recordEventKdown" />
</div> </div>
</Form> </Form>
</Card> </Card>
@@ -153,17 +168,6 @@
<Footer class="layout-footer-center">2022 &copy; flagnote.com</Footer> <Footer class="layout-footer-center">2022 &copy; flagnote.com</Footer>
</Layout> </Layout>
<Modal v-model="modal1" title="Common Modal dialog box title" @on-ok="ok" @on-cancel="cancel">
<p>Content of dialog</p>
<i-switch true-color="#ff4949" @on-change="change">
<Icon type="md-lock" slot="open"></Icon>
<Icon type="md-unlock" slot="close"></Icon>
</i-switch>
<Input v-show="showPassword" maxlength="10" placeholder="Enter password..."
style="width: 150px;margin-left:20px;" />
<p>Content of dialog</p>
</Modal>
<Modal v-model="model.showDelete" width="330" footer-hide class-name="fnmodal" :styles="{ borderRadius: 0 }"> <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;"> <p style="text-align: center;font-size:medium ;margin-bottom: 20px;">
@@ -182,9 +186,7 @@
</template> </template>
<script> <script>
import { unzip, aesDecrypt } from '../libs/secret' import { unwrap } from '../libs/secret'
// import html2canvas from "html2canvas";
import Jquery from "jquery";
import { saveNote } from "@/api/note"; import { saveNote } from "@/api/note";
import { getSecretKey, getStoreKey } from "@/api/lock"; import { getSecretKey, getStoreKey } from "@/api/lock";
import storage from "@/libs/storage"; import storage from "@/libs/storage";
@@ -198,11 +200,6 @@ export default {
components: {}, components: {},
data() { data() {
return { return {
show: true,
loading: false,
modal1: false,
showExt: false,
showPassword: false,
noteForm: { noteForm: {
text: '', text: '',
key: '', key: '',
@@ -211,20 +208,23 @@ export default {
storeKey: '', storeKey: '',
secretKey: '', secretKey: '',
cipher: '', cipher: '',
currentTime: '', password: '',
initTime: '',
}, },
state: { state: {
lock: 0, lock: 0,
locking: 0, locking: 0,
commited: 0 commited: 0,
serverTime: '',
initTime: '',
}, },
model: { model: {
submitting: false,
showDelete: false, showDelete: false,
deleting: false, deleting: false,
showDownloadText: false, showDownloadText: false,
}, },
toTopState: false, toTopState: false,
showMenuState: false,
} }
}, },
created() { created() {
@@ -244,50 +244,41 @@ export default {
// let noteMeta = this.$route.meta.noteMeta; // let noteMeta = this.$route.meta.noteMeta;
let storeKey = getStoreKey(this.noteForm.key); let storeKey = getStoreKey(this.noteForm.key);
this.secret.storeKey = storeKey; this.secret.storeKey = storeKey;
let secretKey = getSecretKey(this.noteForm.key, this.secret.password);
this.secret.secretKey = secretKey;
// first edit // first edit
if (keyMeta) { if (keyMeta) {
this.state.lock = 0; this.state.lock = 0;
this.secret.cipher = keyMeta.cipher; this.secret.cipher = keyMeta.cipher;
this.secret.currentTime = keyMeta.currentTime; this.state.serverTime = keyMeta.serverTime;
// key init Time // key init Time
this.noteForm.initTime = keyMeta.currentTime; this.state.initTime = keyMeta.serverTime;
// clear // clear
storage.local.dynamicClear(); storage.local.dynamicClear();
//if local less 1m ,then deep clear,let local greater than 2m //if local less 1m ,then deep clear,let local greater than 2m
// if (storage.local.getAvailableSize < 1 * 1024 * 1024) { // if (storage.local.getAvailableSize < 1 * 1024 * 1024) {
// storage.local.dynamicDeepClear(this.secret.currentTime, storeKey); // storage.local.dynamicDeepClear(this.state.serverTime, storeKey);
// } // }
storage.local.setText(storeKey + '.text', "0|" + this.secret.cipher + "|0|" + this.noteForm.initTime + "|"); storage.local.setText(storeKey, "0|" + this.secret.cipher + "|0|" + this.state.initTime + "|");
} else { } else {
// second edit // second edit
let storeText = storage.local.getText(storeKey + '.text'); let storeInfo = storage.local.getText(storeKey);
let starray = storeText.split('|'); let starray = storeInfo.split('|');
this.state.lock = parseInt(starray[0]); this.state.lock = parseInt(starray[0]);
this.secret.cipher = starray[1]; this.secret.cipher = starray[1];
this.noteForm.initTime = parseInt(starray[3]); this.state.initTime = parseInt(starray[3]);
}
//TODO: password lock //draw text;
if (this.state.lock == 1) { if (starray[4]) {
this.show = false; this.noteForm.text = unwrap(starray[4], secretKey);
this.noteForm.text = "*****lock*****"; }
return;
} else {
this.loadText();
} }
this.bindCtrlAllEvent(); this.bindCtrlAllEvent();
this.bindToTopEvent(); this.bindToTopEvent();
}, },
mounted() { mounted() {
const myObserver = new ResizeObserver(entries => { const myObserver = new ResizeObserver(entries => {
@@ -322,66 +313,54 @@ export default {
elent.selectionEnd = 0; elent.selectionEnd = 0;
elent.focus(); elent.focus();
}, },
showInput() { refreshPage() {
this.showPassword = true; window.location.reload();
}, },
hideInput() { switchMenu() {
//this.showPassword = false; this.showMenuState = !this.showMenuState;
}, },
change(status) { hideMenu() {
this.showPassword = status; let hbt = document.querySelector('#noteMenu > div > button:hover');
}, if (!hbt) {
ok() { this.showMenuState = false;
}
},
cancel() {
}, },
downLoadText() { downLoadText() {
var blob = new Blob([this.noteForm.text], { type: "application/octet-stream;charset=utf-8" }); var blob = new Blob([this.noteForm.text], { type: "application/octet-stream;charset=utf-8" });
saveAs(blob, this.noteForm.key + ".txt"); saveAs(blob, this.noteForm.key + ".txt");
}, },
loadText() { recordText() {
let storeText = storage.local.getText(this.secret.storeKey + '.text'); let text = this.noteForm.text;
if (text.length > 102400) {
if (null != storeText && '' != storeText) { alert("text length is " + text.length + ",beyond 102400!!!");
let starray = storeText.split('|');
let lock = parseInt(starray[0]);
if (lock == 1) {
alert("note is encrypt!");
return;
}
storeText = starray[4];
if (storeText.length > 0) {
storeText = unzip(storeText);
let secretKey = getSecretKey(this.noteForm.key);
let plainText = aesDecrypt(storeText, secretKey);
if (plainText.startsWith("FLAGNOTE#")) {
this.noteForm.text = plainText.substring(9);
this.state.lock = 0;
} else {
if (this.state.lock == 1) {
alert("password is wrong!")
}
}
} else {
this.noteForm.text = '';
this.state.lock = 0;
}
} }
setStoreText(this.noteForm, this.state, this.secret);
}, },
log() { // recordEventKup(event) {
setStoreText(this.noteForm, this.secret); // let tn = event.currentTarget.value;
}, // let ss = event.currentTarget.selectionStart;
down(event) { // let cline = tn.substring(0, ss).split('\n').length;
if (event.keyCode == 9) {
// var noteTop = parseFloat(window.getComputedStyle(event.currentTarget).lineHeight) * cline;
// var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// if (noteTop < scrollTop + 10) {
// window.scrollTo(0, noteTop - 10);
// }
// },
recordEventKdown(event) {
if (event.key == "Tab") {
let start = event.currentTarget.selectionStart; let start = event.currentTarget.selectionStart;
let end = event.currentTarget.selectionEnd; let end = event.currentTarget.selectionEnd;
let text = event.currentTarget.value; let text = event.currentTarget.value;
if (text.length > 102400) {
alert("text length is " + text.length + ",beyond 102400!!!");
}
let tab = '\t';//\t let tab = '\t';//\t
text = text.substr(0, start) + tab + text.substr(start); text = text.substr(0, start) + tab + text.substr(start);
event.currentTarget.value = text; event.currentTarget.value = text;
@@ -391,138 +370,69 @@ export default {
if (event.preventDefault) { if (event.preventDefault) {
event.preventDefault(); event.preventDefault();
} }
this.noteForm.text = event.currentTarget.value; this.noteForm.text = event.currentTarget.value;
setStoreText(this.noteForm, this.secret); setStoreText(this.noteForm, this.state, this.secret);
} else if (event.ctrlKey && (event.which == 13)) { } else if (event.ctrlKey && (event.which == 13)) {
if (this.state.locking == 1) {
return;
}
if (!this.validateForm()) {
this.state.locking = 0;
return;
}
//save //save
let that = this; let that = this;
this.save().then(res => { this.save().then(res => {
if (res) { if (res) {
let storeText = storage.local.getText(that.secret.storeKey + '.text'); that.state.commited = 1;
let starray = storeText.split("|"); setStoreText(this.noteForm, this.state, this.secret);
storage.local.setText(that.secret.storeKey + '.text', starray[0] + '|' + starray[1] + '|1|' + starray[3] + '|' + starray[4]);
location.reload(); location.reload();
} }
}); });
} }
}, },
save() { save() {
this.noteForm.cipher = this.secret.cipher; return saveNote(this.noteForm, this.secret);
this.noteForm.lock = this.state.lock; },
return saveNote(this.noteForm); validateForm() {
let text = this.noteForm.text;
if (text.length > 102400) {
alert("text length is " + text.length + ",beyond 102400!!!");
return false;
}
if (text.length == 0) {
alert("text is empty!!!");
return false;
}
return true;
}, },
submitNote() { submitNote() {
if (this.state.locking == 1) { if (this.state.locking == 1) {
return; return;
} }
this.state.locking = 1;
this.loading = true; if (!this.validateForm()) {
this.state.locking = 0;
return;
}
this.model.submitting = true;
let that = this; let that = this;
this.save().then(res => { this.save().then(res => {
if (res) { if (res) {
let storeText = storage.local.getText(that.secret.storeKey + '.text'); let storeInfo = storage.local.getText(that.secret.storeKey);
let starray = storeText.split("|"); let starray = storeInfo.split("|");
storage.local.setText(that.secret.storeKey + '.text', starray[0] + '|' + starray[1] + '|1|' + starray[3] + '|' + starray[4]); storage.local.setText(that.secret.storeKey, starray[0] + '|' + starray[1] + '|1|' + starray[3] + '|' + starray[4]);
location.reload(); location.reload();
this.state.locking = 0; this.state.locking = 0;
} }
}); });
},
hideNote() {
if (this.state.locking == 1) {
return;
}
this.state.locking = 1;
if (this.state.lock == 1) {
this.state.locking = 0;
return;
}
let password = '123456';
setStoreText(this.noteForm, this.secret, password);
this.state.lock = 1
this.noteForm.text = '******lock******';
var noteText = document.getElementById("noteText");
noteText.select();
noteText.selectionStart = 0;
noteText.selectionEnd = 1;
this.show = false;
this.state.locking = 0;
},
showNote() {
if (this.state.locking == 1) {
return;
}
this.state.locking = 1;
if (this.state.lock == 0) {
this.state.locking = 0;
return;
}
let password = 'FLAGNOTE';
let storeText = storage.local.getText(this.secret.storeKey + '.text');
if (storeText) {
let lock = parseInt(storeText.substring(0, 1));
if (!lock) {
alert("note is not encrypt!");
}
storeText = storeText.substring(35 + 16);
storeText = unzip(storeText);
let secretKey = getSecretKey(this.noteForm.key, password);
let plainText = aesDecrypt(storeText, secretKey);
if (plainText.startsWith("FLAGNOTE#")) {
lock = "0";
} else {
let secretKey = getSecretKey(this.noteForm.key);
let plainText = aesDecrypt(storeText, secretKey);
if (plainText.startsWith("FLAGNOTE#")) {
lock = "0";
} else {
lock = "1";
}
}
if (lock == '0') {
this.noteForm.text = plainText.substring(9);
this.state.lock = "0";
setStoreText(this.noteForm, this.secret);
this.show = true;
this.state.locking = "0";
} else {
alert("password is wrong!")
this.state.locking = "0";
return;
}
} else {
alert("storeText is deleted!")
this.state.locking = "0";
}
}, },
refresh() { refresh() {
location.reload(); location.reload();
@@ -535,10 +445,8 @@ export default {
}, },
dropNote() { dropNote() {
this.model.deleting = true; this.model.deleting = true;
storage.local.delete(this.secret.storeKey + '.text'); storage.local.delete(this.secret.storeKey);
storage.session.delete(this.secret.storeKey + '.keyMeta');
location.reload(); location.reload();
}, },
bindToTopEvent() { bindToTopEvent() {
let that = this; let that = this;
@@ -549,40 +457,18 @@ export default {
} else { } else {
that.toTopState = false; that.toTopState = false;
} }
//document.getElementsByClassName("ivu-affix")[0]
} }
}, },
bindCtrlAllEvent() { bindCtrlAllEvent() {
if (document.body.createTextRange) { if (window.getSelection && document.createRange) {
Jquery(document).keydown(function (e) { document.onkeydown = (e) => {
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) { if ((e.ctrlKey || e.metaKey) && e.key == "a") {
e.preventDefault(); e.preventDefault();
var element = document.getElementById("noteText"); var element = document.getElementById("noteText");
element.select(); element.select();
} }
}); }
} else if (window.getSelection) {
Jquery(document).keydown(function (e) {
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {
e.preventDefault();
var element = document.getElementById("noteText");
element.select();
}
});
} else {
//alert('none');
} }
} }
} }
} }

View File

@@ -133,32 +133,34 @@ button span {
margin-left: -1px !important; margin-left: -1px !important;
margin-bottom: 4px; margin-bottom: 4px;
} }
/* #noteMenu button span{
font-size: 15px;
line-height: 20px;
margin-left: 7px !important;
margin-right: 5px !important;
} */
</style> </style>
<template> <template>
<div class="layout"> <div class="layout">
<Layout> <Layout>
<Header class="header"> <Header class="header">
<Row> <Row>
<Col :xs="{ span: 24, offset: 0 }" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }" <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 }"> :lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
<Affix :offset-top="0"> <Affix :offset-top="0">
<div style="background: white;width:100%;height:40px;"> <div style="background: white;width:100%;height:40px;">
<img style="height:40px;float:left;" alt="refresh flagnote" src="/static/favicon.png"> <img style="height:40px;float:left;cursor: pointer;" alt="refresh flagnote" src="/static/favicon.png"
v-on:click="refreshPage()">
<div style="float:left;width:auto;"> <div style="float:left;width:auto;">
<Button-group size="large"> <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 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>
@@ -168,29 +170,51 @@ button span {
<div style="float:right;width:auto;"> <div style="float:right;width:auto;">
<Button-group size="large"> <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 aria-label="create note" type="error" <Button aria-label="share" type="error"
style="margin-left:10px; border-radius: 0px;font-size: 24px;" @click="createNote()" style="margin-left:5px; border-radius: 0px;font-size: 24px; font-family: Arial, sans-serif"
icon="md-add"></Button> @click="showShareModel()" icon="md-cloud-done">{{ state.ttlDesc }}</Button>
<Button aria-label="delete note" type="error"
style="margin-left:5px; border-radius: 0px;font-size: 24px;" @click="showDeleteModel()" <Button aria-label="menu" type="error" style="margin-left:5px; border-radius: 0px;font-size: 24px;"
icon="md-trash"></Button> @click="switchMenu()" @blur.native="hideMenu()" icon="md-menu"></Button>
</Button-group> </Button-group>
</div> </div>
</div>
<div id="noteMenu" :class="[showMenuState ? 'showBlock' : 'hideBlock']"
style="z-index: 100; position: absolute;top:41px;right:0px;left:auto;">
<Button-group vertical size="large">
<Button type="error" icon="md-add" style="border-radius: 0px;font-size: 24px;"
@click="createNote(); switchMenu();"></Button>
<!--
<Button type="error" icon="md-refresh" style="border-radius: 0px;font-size: 24px;"
@click="refreshPage()"></Button>
-->
<Button aria-label="download text" v-show="model.showDownloadText" type="error"
style="border-radius: 0px;font-size: 24px;" @click="downLoadText(); switchMenu();"
icon="md-download"></Button>
<Button type="error" icon="md-trash" style="border-radius: 0px;font-size: 24px;"
@click="showDeleteModel(); switchMenu();"></Button>
</Button-group>
</div> </div>
</Affix> </Affix>
</Col> </Col>
</Row> </Row>
</Header> </Header>
<Content class="content"> <Content class="content">
<div style="min-height: 650px;"> <div style="min-height: 650px;">
@@ -198,10 +222,7 @@ button span {
<Col :xs="{ span: 24, offset: 0 }" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }" <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 }"> :lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
<Card :padding="0"> <Card :padding="0">
<div id="wrapper" style="border-left: 0px solid #FF3366;" @contextmenu="showMenu"> <div id="wrapper" style="border-left: 0px solid #FF3366;">
<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" <div id="noteText" style="text-align: left;min-height: 650px;" class="monoFt"
v-html="noteForm.escapeText"> v-html="noteForm.escapeText">
</div> </div>
@@ -213,8 +234,6 @@ button span {
</Content> </Content>
<Footer class="layout-footer-center"> <Footer class="layout-footer-center">
2022 &copy; flagnote.com 2022 &copy; flagnote.com
</Footer> </Footer>
@@ -237,7 +256,8 @@ button span {
{{ $t("message.askTodelete") }} {{ $t("message.askTodelete") }}
</p> </p>
<p style="text-align: center;"> <p style="text-align: center;">
<Button type="error" :loading="model.delete" style="border-radius: 0px;" @click="dropNote()">{{ $t("button.yes") <Button type="error" :loading="model.deleting" style="border-radius: 0px;" @click="dropNote()">{{
$t("button.yes")
}}</Button> }}</Button>
</p> </p>
</Modal> </Modal>
@@ -250,10 +270,9 @@ button span {
<script> <script>
import { aesDecrypt, md5, unzip } from "@/libs/secret"; import { md5, unwrap } from "@/libs/secret";
import Jquery from "jquery"; import { getStoreKey, getSecretKey } from "@/api/lock";
import { getSecretKey, getStoreKey } from "@/api/lock"; import { deleteNote, getNoteBlob } from "@/api/note";
import { deleteNote, getNote } from "@/api/note";
import storage from "@/libs/storage"; import storage from "@/libs/storage";
import { getEscapeText } from "@/libs/noteStorage"; import { getEscapeText } from "@/libs/noteStorage";
import QRCode from "qrcode"; import QRCode from "qrcode";
@@ -273,23 +292,20 @@ export default {
text: '', text: '',
escapeText: '', escapeText: '',
key: '', key: '',
md5: '',
lock: 0,
ttl: 3600,
ttlDesc: '-- : --',
}, },
secret: { secret: {
storeKey: '', storeKey: '',
secretKey: '', secretKey: '',
cipher: '', cipher: '',
currentTime: '', md5: '',
}, },
state: { state: {
lock: 0, lock: null,
locking: 0,
initTime: null, initTime: null,
initTtl: null, initTtl: null,
commited: 0, ttl: null,
ttlDesc: '-- : --',
serverTime: null,
}, },
model: { model: {
showDelete: false, showDelete: false,
@@ -298,26 +314,8 @@ export default {
showDownloadText: false, showDownloadText: false,
}, },
toTopState: false, toTopState: false,
contextMenuData: { showMenuState: false,
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,
} }
}, },
@@ -337,15 +335,18 @@ export default {
this.noteForm.noteUrl = getNoteUrl(this.noteForm.key); this.noteForm.noteUrl = getNoteUrl(this.noteForm.key);
this.secret.storeKey = getStoreKey(this.noteForm.key); this.secret.storeKey = getStoreKey(this.noteForm.key);
this.secret.secretKey = getSecretKey(this.noteForm.key, this.secret.password);
if (noteMeta) { if (noteMeta) {
this.state.lock = noteMeta.lock; this.state.lock = noteMeta.lock;
this.state.initTime = new Date().getTime(); this.state.initTime = new Date().getTime();
this.state.initTtl = noteMeta.ttl; this.state.initTtl = noteMeta.ttl;
this.secret.currentTime = noteMeta.currentTime; this.state.ttl = noteMeta.ttl;
this.state.serverTime = noteMeta.serverTime;
this.secret.cipher = "00000000000000000000000000000000";//noteMeta.cipher; //读者有没有值可配置 this.secret.cipher = "00000000000000000000000000000000";//noteMeta.cipher; //读者有没有值可配置
this.noteForm.md5 = noteMeta.md5; this.secret.md5 = noteMeta.md5;
this.noteForm.ttl = noteMeta.ttl;
this.startClock(); this.startClock();
@@ -356,7 +357,6 @@ export default {
this.bindCtrlAllEvent(); this.bindCtrlAllEvent();
this.bindCopyUrlEvent(); this.bindCopyUrlEvent();
this.bindToTopEvent(); this.bindToTopEvent();
this.bindMouseEvent();
} else { } else {
alert("Unconnected."); alert("Unconnected.");
@@ -364,7 +364,7 @@ export default {
}, },
mounted() { mounted() {
this.bindCopyTextEvent(); //this.bindCopyTextEvent();
const myObserver = new ResizeObserver(entries => { const myObserver = new ResizeObserver(entries => {
// iterate over the entries, do something. // iterate over the entries, do something.
@@ -390,29 +390,13 @@ export default {
computed: {}, computed: {},
watch: {}, watch: {},
methods: { methods: {
selectAllText() { switchMenu() {
var element = document.getElementById("noteText"); this.showMenuState = !this.showMenuState;
if (window.getSelection) {
let selection = window.getSelection();
let range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
}, },
copySelectedText() { hideMenu() {
let hbt = document.querySelector('#noteMenu > div > button:hover');
}, if (!hbt) {
copyAllText() { this.showMenuState = false;
},
showMenu(event) {
event.preventDefault()
var x = event.clientX
var y = event.clientY
// Get the current location
this.contextMenuData.axis = {
x, y
} }
}, },
downLoadText() { downLoadText() {
@@ -425,7 +409,7 @@ export default {
startClock() { startClock() {
let that = this; let that = this;
window.setInterval(function () { window.setInterval(function () {
let ittl = parseInt(that.noteForm.ttl / 1000); let ittl = parseInt(that.state.ttl / 1000);
let mins = parseInt(ittl / 60); let mins = parseInt(ittl / 60);
if (mins < 0) { if (mins < 0) {
mins = "00"; mins = "00";
@@ -440,20 +424,17 @@ export default {
} else if (seds < 10) { } else if (seds < 10) {
seds = "0" + seds; seds = "0" + seds;
} }
that.noteForm.ttlDesc = mins + ":" + seds; that.state.ttlDesc = mins + ":" + seds;
that.noteForm.ttl = that.state.initTtl - (new Date().getTime() - that.state.initTime); that.state.ttl = that.state.initTtl - (new Date().getTime() - that.state.initTime);
if (that.noteForm.ttl <= 0) { if (that.state.ttl <= 0) {
storage.local.delete(that.secret.storeKey + '.text'); storage.local.delete(that.secret.storeKey);
location.reload(); location.reload();
} }
}, 1000) }, 1000)
}, },
decryptNote() { refreshPage() {
// let password = "123456"; window.location.reload();
// let secretKey = getSecretKey(this.noteForm.key, password);
// this.loadText(secretKey);
}, },
createNote() { createNote() {
window.open("/"); window.open("/");
@@ -484,7 +465,7 @@ export default {
let that = this; let that = this;
deleteNote(this.noteForm.key).then(res => { deleteNote(this.noteForm.key).then(res => {
if (res) { if (res) {
storage.local.delete(that.secret.storeKey + '.text'); storage.local.delete(that.secret.storeKey);
location.reload(); location.reload();
} else { } else {
that.model.deleting = false; that.model.deleting = false;
@@ -492,36 +473,58 @@ export default {
}); });
}, },
loadText() { loadText() {
let password = ''; let that = this;
if (this.noteForm.lock == 1) { let storeInfo = storage.local.getText(this.secret.storeKey);
password = "FLAGNOTE"; //默认密码
let starray = [];
if (storeInfo) {
starray = storeInfo.split('|');
} }
let secretKey = getSecretKey(this.noteForm.key, password); if (!storeInfo || !starray[4] || (md5(starray[4]) != this.secret.md5)) {
let storeText = storage.local.getText(this.secret.storeKey + '.text');
if (!storeText || (md5(storeText.substring(51)) != this.noteForm.md5)) {
// local is useless // local is useless
let note = getNote(this.noteForm.key); getNoteBlob(this.noteForm.key).then((res) => {
// if lack of local , not set local if (!res.data || res.data.size == 0) {
if (storage.local.getAvailableSize() > 1 * 1024 * 1024) { return;
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('|');
if ("0" == starray[2]) {
storage.local.setText(this.secret.storeKey + '.text', starray[0] + "|" + starray[1] + "|1|" + starray[3] + "|" + starray[4]);
}
}
if (storeText) { let blob = new Blob([res.data], {
storeText = unzip(storeText.split('|')[4]); type: res.data.type
let plainText = aesDecrypt(storeText, secretKey); });
if (plainText.startsWith("FLAGNOTE#")) {
this.noteForm.text = plainText.substring(9); let reader = new FileReader();
this.noteForm.escapeText = getEscapeText(this.noteForm.text); reader.onload = function (e) {
if (!e.target.result || e.target.result.byteLength == 0) {
return;
}
var bytes = new Uint8Array(e.target.result);
let bytesString = bytes.join(",");
that.noteForm.text = unwrap(bytesString, that.secret.secretKey);
that.noteForm.escapeText = getEscapeText(that.noteForm.text);
// if local is enough, set local
if (storage.local.getAvailableSize() > 1 * 1024 * 1024) {
storage.local.setText(that.secret.storeKey, that.state.lock + '|' + that.secret.cipher + '|1|' + that.state.serverTime + '|' + bytesString);
}
};
reader.readAsArrayBuffer(blob);
});
} else {
starray = storeInfo.split('|');
if (!starray[4]) {
return;
}
this.noteForm.text = unwrap(starray[4], this.secret.secretKey);
this.noteForm.escapeText = getEscapeText(this.noteForm.text);
// local is usable, and set commited flag
if ("0" == starray[2]) {
storage.local.setText(this.secret.storeKey, starray[0] + "|" + starray[1] + "|1|" + starray[3] + "|" + starray[4]);
} }
} }
}, },
@@ -540,97 +543,28 @@ export default {
let that = this; let that = this;
var clipboard = new Clipboard("#tag-copy") var clipboard = new Clipboard("#tag-copy")
clipboard.on('success', function () { clipboard.on('success', function () {
that.$Message.success({ content: 'url copied' }); that.$Message.success({ content: 'url copied.' });
}); });
clipboard.on('error', function () { clipboard.on('error', function () {
that.$Message.error('not allow to copy'); 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;
}
});
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');
}); });
}, },
bindCtrlAllEvent() { bindCtrlAllEvent() {
if (window.getSelection) { if (window.getSelection && document.createRange) {
document.onkeydown = (e) => {
Jquery(document).keydown(function (e) { if ((e.ctrlKey || e.metaKey) && e.key == "a") {
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {
e.preventDefault(); e.preventDefault();
var element = document.getElementById("noteText"); var element = document.getElementById("noteText");
let selection = window.getSelection(); let selection = window.getSelection();
let range = document.createRange(); let range = document.createRange();
range.selectNodeContents(element); range.selectNodeContents(element);
selection.removeAllRanges(); selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
}
});
}
},
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) }
} },
} }
} }
</script> </script>

View File

@@ -5,7 +5,7 @@ module.exports = defineConfig({
assetsDir:'static', assetsDir:'static',
productionSourceMap: false, productionSourceMap: false,
configureWebpack: { configureWebpack: {
devtool: false, // devtool: false,
plugins: [ plugins: [
new CompressionPlugin({ new CompressionPlugin({
algorithm: "gzip", // 使用gzip压缩 algorithm: "gzip", // 使用gzip压缩
@@ -27,7 +27,7 @@ module.exports = defineConfig({
devServer: { devServer: {
proxy: { proxy: {
"/note": { "/note": {
target: "http://12coffee.club:3333/", // 后台接口域名 target: "http://localhost:3333/", // 后台接口域名
secure: false, // 如果是https接口需要配置这个参数 secure: false, // 如果是https接口需要配置这个参数
changeOrigin: true, //是否跨域 changeOrigin: true, //是否跨域
pathRewrite: { pathRewrite: {