memos.html 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta
  6. name="viewport"
  7. content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"
  8. />
  9. <title>memos记录</title>
  10. <script src="js/util.js"></script>
  11. <script src="js/login.js"></script>
  12. <style>
  13. body {
  14. font-family: Lyon-Text, Georgia, "Songti SC", SimSun, serif;
  15. }
  16. .vditor-reset {
  17. font-family: Lyon-Text, Georgia, "Songti SC", SimSun, serif !important;
  18. }
  19. img {
  20. border: 1px solid #b7b6b6;
  21. }
  22. .img {
  23. border: 1px solid #b7b6b6;
  24. max-width: 200px;
  25. max-height: 200px;
  26. margin: 10px 10px 0 0 !important;
  27. cursor: pointer;
  28. }
  29. .editClass button {
  30. display: inline;
  31. /*margin-top: 5px;*/
  32. margin-left: 30px;
  33. }
  34. .contentLine div {
  35. margin: 5px 0 0;
  36. }
  37. .border {
  38. /*border: 1px solid #b7b6b6;*/
  39. border-radius: 5px;
  40. padding: 10px;
  41. background-color: white;
  42. }
  43. body {
  44. background-color: #f4f4f5;
  45. }
  46. .myTop {
  47. position: fixed;
  48. top: 0;
  49. background-image: url("./img/gray.png");
  50. /* 背景铺满整个div*/
  51. background-size: 100% 100%;
  52. width: 100%;
  53. padding: 10px;
  54. z-index: 100;
  55. }
  56. .myText {
  57. border: 1px solid #d2d2d2;
  58. }
  59. .vditor-reset {
  60. padding: 10px 20px !important;
  61. }
  62. .vditor-toolbar {
  63. display: none;
  64. }
  65. </style>
  66. <script src="js/vue.min.js"></script>
  67. <script src="elementUI/index.js"></script>
  68. <link rel="stylesheet" href="elementUI/index.css" />
  69. <link rel="stylesheet" href="css/vditor.css" />
  70. <script src="js/vditor.js"></script>
  71. </head>
  72. <body>
  73. <div id="app">
  74. <div id="all" style="max-width: 1500px; margin: 0 auto">
  75. <div class="myTop">
  76. <el-link href="https://memos.tianyunperfect.cn/">memos主页</el-link>
  77. <el-button @click="page = page - 1;search()" :disabled="page === 1"
  78. >上一页</el-button
  79. >
  80. <el-input
  81. type="number"
  82. style="width: 80px"
  83. v-model="page"
  84. @change="search"
  85. ></el-input>
  86. <el-button @click="page = page + 1;search()">下一页</el-button>
  87. <el-select
  88. v-model="size"
  89. placeholder="请选择"
  90. style="width: 80px"
  91. @change="search"
  92. >
  93. <el-option
  94. v-for="item in [10, 20, 50, 100]"
  95. :key="item"
  96. :label="item"
  97. :value="item"
  98. >
  99. </el-option>
  100. </el-select>
  101. <!--输入框,绑定回车事件,触发查询-->
  102. <el-input
  103. style="margin-left: 30px; width: 20vw; padding-left: 10px"
  104. placeholder="关键字"
  105. v-model="searchStr"
  106. @change="searchStrChange"
  107. ></el-input>
  108. <el-input
  109. style="margin-left: 30px; width: 20vw; padding-left: 10px"
  110. placeholder="AI 问答"
  111. v-model="searchStr2"
  112. @change="searchStrChange2"
  113. ></el-input>
  114. <br /><br />
  115. <div></div>
  116. <div style="display: flex; align-items: stretch; width: 60vw">
  117. <!-- 创建一个用于编辑的容器 -->
  118. <el-input
  119. @paste.native="handlePaste"
  120. @keydown.enter.native="keyDown"
  121. type="textarea"
  122. :autosize="{minRows:2}"
  123. placeholder="记录内容"
  124. @input="localStorage.setItem('contentStr', contentStr)"
  125. v-model="contentStr"
  126. >
  127. </el-input>
  128. <!-- 添加发送按钮 -->
  129. <el-button
  130. style="margin-left: 20px; width: 10%; font-size: 2rem"
  131. @click="sendData"
  132. :disabled="!sendBtnAble"
  133. >记录</el-button
  134. >
  135. <el-button
  136. style="margin-left: 20px; width: 10%; font-size: 1rem"
  137. @click="mergeByIds"
  138. >合并</el-button
  139. >
  140. <div
  141. @click="dialogVisible2=true"
  142. style="font-size: 0.3em; margin-left: 2px"
  143. >
  144. 替换
  145. </div>
  146. </div>
  147. <div style="display: flex">
  148. <div v-for="item in tmpFileIds">
  149. <el-image
  150. v-if="item['type'].startsWith('image')"
  151. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path'] + '?width=150'"
  152. :preview-src-list="['https://memos_assert.tianyunperfect.cn/' + item['internal_path'] + '?width=750']"
  153. class="img"
  154. ></el-image>
  155. <audio controls v-if="item['type'].startsWith('audio')">
  156. <source
  157. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  158. type="audio/ogg"
  159. />
  160. <source
  161. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  162. type="audio/mpeg"
  163. />
  164. 您的浏览器不支持 audio 元素。
  165. </audio>
  166. <video
  167. width="320"
  168. height="240"
  169. controls
  170. v-if="item['type'].startsWith('video')"
  171. >
  172. <source
  173. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  174. type="video/mp4"
  175. />
  176. <source
  177. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  178. type="video/ogg"
  179. />
  180. 您的浏览器不支持 HTML5 video 元素。
  181. </video>
  182. <el-link
  183. v-if="item['type'].startsWith('application')"
  184. :href="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  185. target="_blank"
  186. >{{item['filename']}}</el-link
  187. >
  188. <div @click="removeTmp(item)">删除</div>
  189. </div>
  190. </div>
  191. <!-- 添加多选按钮 -->
  192. <div style="display: flex">
  193. <div style="max-width: 55vw; height: 2.5em; overflow-y: scroll">
  194. <el-checkbox-group v-model="tagChecked" @change="checkChange1()">
  195. <el-checkbox-button
  196. v-for="tag in allTags"
  197. :label="tag"
  198. ></el-checkbox-button>
  199. </el-checkbox-group>
  200. </div>
  201. <div style="margin-left: 20px; display: flex; align-items: center">
  202. <el-switch
  203. @change="duoxuanChange"
  204. v-model="duoxuan"
  205. active-text="多选"
  206. inactive-text="单选"
  207. >
  208. </el-switch>
  209. </div>
  210. </div>
  211. <div id="tags"></div>
  212. </div>
  213. <div style="margin-top: 180px"></div>
  214. <div v-if="searchStr2" style="margin: 10px; padding: 10px">
  215. <el-input
  216. v-model="searchResStr"
  217. type="textarea"
  218. :autosize="{minRows: 1}"
  219. readonly="true"
  220. ></el-input>
  221. </div>
  222. <div>
  223. <div v-for="item in contentList">
  224. <el-row style="margin: 10px; padding: 10px" class="border">
  225. <el-col :span="18" class="contentLine">
  226. <div style="display: flex">
  227. <el-date-picker
  228. v-model="item.created_ts"
  229. type="datetime"
  230. readonly="true"
  231. placeholder="选择日期时间"
  232. >
  233. </el-date-picker>
  234. <div style="margin-left: 20px">
  235. <el-checkbox-button
  236. v-for="tag in commonTags"
  237. :label="tag"
  238. :key="item"
  239. @change="checked=>checkChange(checked,item,tag)"
  240. :checked="isChooseTag(item,tag)"
  241. >{{tag}}
  242. </el-checkbox-button>
  243. </div>
  244. </div>
  245. <div class="myText" @dblclick="edit(item)">
  246. <!-- <el-input v-model="item.content" type="textarea" :autosize="{minRows: 1}" readonly="true"></el-input>-->
  247. <div :id="item.resource_name"></div>
  248. </div>
  249. <div style="display: flex">
  250. <div v-for="item in resourceMap[item.id]">
  251. <el-image
  252. v-if="item['type'].startsWith('image')"
  253. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path'] + '?width=150'"
  254. :preview-src-list="['https://memos_assert.tianyunperfect.cn/' + item['internal_path'] + '?width=750']"
  255. fit="cover"
  256. class="img"
  257. >
  258. </el-image>
  259. <audio controls v-if="item['type'].startsWith('audio')">
  260. <source
  261. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  262. type="audio/ogg"
  263. />
  264. <source
  265. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  266. type="audio/mpeg"
  267. />
  268. 您的浏览器不支持 audio 元素。
  269. </audio>
  270. <video
  271. width="320"
  272. height="240"
  273. controls
  274. v-if="item['type'].startsWith('video')"
  275. >
  276. <source
  277. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  278. type="video/mp4"
  279. />
  280. <source
  281. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  282. type="video/ogg"
  283. />
  284. 您的浏览器不支持 HTML5 video 元素。
  285. </video>
  286. <el-link
  287. v-if="item['type'].startsWith('application')"
  288. :href="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  289. target="_blank"
  290. >{{item['filename']}}</el-link
  291. >
  292. </div>
  293. </div>
  294. </el-col>
  295. <el-col :span="4" style="display: flex">
  296. <div class="editClass">
  297. <el-button
  298. size="small"
  299. @click="window.open('https://memos.tianyunperfect.cn/m/' + item.resource_name)"
  300. >跳转</el-button
  301. >
  302. <br />
  303. <br />
  304. <el-button size="small" @click="edit(item)">编辑</el-button>
  305. </div>
  306. <div class="editClass">
  307. <el-button size="small" @click="archive(item)"
  308. >归档</el-button
  309. >
  310. <br />
  311. <br />
  312. <el-button size="small" type="warning" @click="del(item)"
  313. >删除</el-button
  314. >
  315. </div>
  316. <div class="editClass" style="margin-left: 30px">
  317. <el-checkbox
  318. :value="selectedIds.includes(item.id)"
  319. @change="handleCheckboxChange(item.id, $event)"
  320. >合并</el-checkbox
  321. >
  322. </div>
  323. </el-col>
  324. </el-row>
  325. </div>
  326. </div>
  327. </div>
  328. <el-dialog title="提示" :visible.sync="dialogVisible" width="60%">
  329. <div v-if="tmpItem.id" class="contentLine">
  330. <div>
  331. <el-date-picker
  332. v-model="tmpItem.created_ts"
  333. type="datetime"
  334. value-format="yyyy-MM-ddTHH:mm:ss"
  335. placeholder="选择日期时间"
  336. >
  337. </el-date-picker>
  338. </div>
  339. <div>
  340. <el-input
  341. @paste.native="handlePaste2"
  342. ref="contentEdit"
  343. v-model="tmpItem.content"
  344. type="textarea"
  345. :autosize="{minRows: 2}"
  346. ></el-input>
  347. </div>
  348. <div style="display: flex">
  349. <div v-for="item in cloneFileIds">
  350. <el-image
  351. v-if="item['type'].startsWith('image')"
  352. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path'] + '?width=150'"
  353. :preview-src-list="['https://memos_assert.tianyunperfect.cn/' + item['internal_path'] + '?width=750']"
  354. class="img"
  355. >
  356. </el-image>
  357. <audio controls v-if="item['type'].startsWith('audio')">
  358. <source
  359. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  360. type="audio/ogg"
  361. />
  362. <source
  363. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  364. type="audio/mpeg"
  365. />
  366. 您的浏览器不支持 audio 元素。
  367. </audio>
  368. <video
  369. width="320"
  370. height="240"
  371. controls
  372. v-if="item['type'].startsWith('video')"
  373. >
  374. <source
  375. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  376. type="video/mp4"
  377. />
  378. <source
  379. :src="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  380. type="video/ogg"
  381. />
  382. 您的浏览器不支持 HTML5 video 元素。
  383. </video>
  384. <el-link
  385. v-if="item['type'].startsWith('application')"
  386. :href="'https://memos_assert.tianyunperfect.cn/' + item['internal_path']"
  387. target="_blank"
  388. >{{item['filename']}}</el-link
  389. >
  390. <div @click="delPic(item)">删除</div>
  391. </div>
  392. </div>
  393. </div>
  394. <span slot="footer" class="dialog-footer">
  395. <el-button @click="dialogVisible = false">取 消</el-button>
  396. <el-button type="primary" @click="confirmSubmit">确 定</el-button>
  397. </span>
  398. </el-dialog>
  399. <el-dialog
  400. title="替换(危险操作,不可撤销)"
  401. :visible.sync="dialogVisible2"
  402. width="60%"
  403. >
  404. <div class="contentLine">
  405. <div>
  406. <el-input v-model="old_text" placeholder="old"></el-input>
  407. <el-input v-model="new_text" placeholder="new"></el-input>
  408. </div>
  409. </div>
  410. <span slot="footer" class="dialog-footer">
  411. <el-button @click="dialogVisible2 = false">取 消</el-button>
  412. <el-button type="primary" @click="confirmSubmit2">确 定</el-button>
  413. </span>
  414. </el-dialog>
  415. </div>
  416. <!-- 引入Quill库 -->
  417. <!--<script src="./js/cdn.quilljs.com_1.3.6_quill.js"></script>-->
  418. <!--异步请求示例:requestUtil.sync('https://jsonplaceholder.typicode.com/posts/1', 'post', data, headers) .then(data => console.log(data))-->
  419. <script>
  420. // if (getQueryString("id") !== "tianyunperfect") {
  421. // // location.href = "http://127.0.0.1";
  422. // }
  423. let authStr =
  424. "bearer eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJuYW1lIjoidGlhbnl1bnBlcmZlY3QiLCJpc3MiOiJtZW1vcyIsInN1YiI6IjEiLCJhdWQiOlsidXNlci5hY2Nlc3MtdG9rZW4iXSwiaWF0IjoxNzA5MTc5NTUyfQ.LFxWB4efya1sL7VoJ42xpXxbAip-udT_Kx2OwZ8Y3-E";
  425. let myHeaders = {
  426. "Content-type": "application/json",
  427. Authorization: authStr,
  428. };
  429. function getRandomId() {
  430. return Math.random().toString(36).substr(2);
  431. }
  432. let vm = new Vue({
  433. el: "#app",
  434. data: {
  435. dialogVisible2: false,
  436. old_text: "",
  437. new_text: "",
  438. searchStr2: "",
  439. searchResStr: "",
  440. selectedIds: [],
  441. duoxuan: false,
  442. tmpItem: {},
  443. dialogVisible: false,
  444. page: 1,
  445. size: 20,
  446. searchStr: "",
  447. contentStr: "",
  448. tagChecked: [],
  449. allTags: [],
  450. contentList: [],
  451. sendBtnAble: true,
  452. resourceMap: {}, // 资源map,
  453. commonTags: ["todo"],
  454. upload_url: "https://memos.tianyunperfect.cn/api/v1/resource/blob",
  455. tmpFileIds: [],
  456. cloneFileIds: [],
  457. },
  458. watch: {
  459. page: function () {
  460. this.search();
  461. },
  462. size: function () {
  463. this.search();
  464. },
  465. contentList: function () {
  466. console.log(1);
  467. this.$nextTick(() => {
  468. console.log(2);
  469. this.contentList.forEach((item) => {
  470. this.resetView(item);
  471. // let ed = new Vditor(item.resource_name, {
  472. // cdn: 'https://ld246.com/js/lib/vditor',
  473. // // height: 360,
  474. // toolbarConfig: {
  475. // pin: true,
  476. // },
  477. // // preview: {
  478. // // theme:{
  479. // // path: ''
  480. // // }
  481. // // },
  482. // theme: 'classic',
  483. // cache: {
  484. // enable: false,
  485. // },
  486. // after: () => {
  487. // ed.setValue(item.content)
  488. // },
  489. // })
  490. });
  491. });
  492. },
  493. },
  494. mounted() {
  495. if (!checkLogin()) {
  496. return;
  497. }
  498. if (this.getQueryStr()) {
  499. this.searchStr = this.getQueryStr();
  500. }
  501. this.duoxuan = this.getDuoXuan();
  502. // 初始化contentStr,如果本地有缓存
  503. let item = localStorage.getItem("contentStr");
  504. if (item) {
  505. this.contentStr = item;
  506. }
  507. let queryStr = this.getQueryStr();
  508. if (queryStr) {
  509. // queryStr 解码
  510. queryStr = decodeURIComponent(queryStr);
  511. this.searchStr = queryStr;
  512. }
  513. // 解码
  514. let tagsStr = decodeURIComponent(getQueryString("tags"));
  515. if (tagsStr) {
  516. this.tagChecked = tagsStr.split(",");
  517. }
  518. // this.getAllTags();
  519. this.search();
  520. requestUtil
  521. .async(
  522. "https://web_history.tianyunperfect.cn/memos/getCustomMemoTags",
  523. "get"
  524. )
  525. .then((res) => {
  526. this.commonTags = JSON.parse(res["res"][0]["content"]);
  527. this.allTags = this.commonTags;
  528. });
  529. },
  530. methods: {
  531. resetView(item) {
  532. Vditor.preview(
  533. document.getElementById(item.resource_name),
  534. item.content,
  535. {
  536. cdn: "https://ld246.com/js/lib/vditor",
  537. speech: {
  538. enable: false,
  539. },
  540. anchor: 1,
  541. after() {},
  542. }
  543. );
  544. },
  545. mergeByIds() {
  546. if (this.selectedIds.length < 2) {
  547. return;
  548. }
  549. requestUtil.sync(
  550. "https://web_history.tianyunperfect.cn/memos/mergeMemo",
  551. "post",
  552. { ids: this.selectedIds },
  553. {}
  554. );
  555. location.reload();
  556. },
  557. handleCheckboxChange(id, checked) {
  558. if (checked) {
  559. this.selectedIds.push(id);
  560. } else {
  561. this.selectedIds = this.selectedIds.filter(
  562. (selectedId) => selectedId !== id
  563. );
  564. }
  565. console.log(this.selectedIds);
  566. },
  567. searchStrChange() {
  568. let queryStr = this.searchStr;
  569. // url编码
  570. queryStr = encodeURIComponent(queryStr);
  571. location.href = requestUtil.buildUrl(location.href, {
  572. queryStr: queryStr,
  573. });
  574. },
  575. searchStrChange2() {
  576. this.searchResStr = "";
  577. requestUtil
  578. .async(
  579. "https://wx.tianyunperfect.cn/full_search?",
  580. "post",
  581. { search: this.searchStr2 },
  582. {}
  583. )
  584. .then((res) => {
  585. if (res["list"]) {
  586. let resList = res["list"];
  587. this.searchResStr = res["res"];
  588. this.contentList = resList;
  589. this.searchByContent();
  590. }
  591. });
  592. },
  593. duoxuanChange() {
  594. if (this.duoxuan) {
  595. this.setDuoXuan("ok");
  596. } else {
  597. this.setDuoXuan("");
  598. }
  599. },
  600. checkChange1(newVal) {
  601. console.log(this.tagChecked);
  602. if (!this.getDuoXuan()) {
  603. if (this.tagChecked.length > 1) {
  604. // 保留最后一个
  605. this.tagChecked = [this.tagChecked[this.tagChecked.length - 1]];
  606. }
  607. }
  608. location.href = requestUtil.buildUrl(location.href, {
  609. tags: this.tagChecked.join(","),
  610. });
  611. },
  612. getQueryStr() {
  613. return getQueryString("queryStr");
  614. },
  615. getDuoXuan() {
  616. return getQueryString("duoxuan") == "ok";
  617. },
  618. setDuoXuan(ok) {
  619. location.href = requestUtil.buildUrl(location.href, {
  620. duoxuan: ok,
  621. });
  622. },
  623. removeTmp(item) {
  624. // tmpFileIds 根据id删除
  625. let index = this.tmpFileIds.findIndex(
  626. (item) => item.id === item.id
  627. );
  628. if (index !== -1) {
  629. this.tmpFileIds.splice(index, 1);
  630. }
  631. },
  632. delPic(img) {
  633. // 根据id从 cloneFileIds 删除
  634. let index = this.cloneFileIds.findIndex(
  635. (item) => item.id === img.id
  636. );
  637. if (index !== -1) {
  638. this.cloneFileIds.splice(index, 1);
  639. }
  640. },
  641. getCloneResourceIds(id) {
  642. let resourceMapElement = this.resourceMap[id];
  643. if (resourceMapElement) {
  644. return JSON.parse(JSON.stringify(resourceMapElement));
  645. }
  646. return [];
  647. },
  648. uploadFile(url, file, headers = {}) {
  649. return new Promise((resolve, reject) => {
  650. const xhr = new XMLHttpRequest();
  651. xhr.open("POST", url, true); // 使用异步的方式
  652. const formData = new FormData();
  653. formData.append("file", file);
  654. for (const [key, value] of Object.entries(headers)) {
  655. xhr.setRequestHeader(key, value);
  656. }
  657. xhr.onload = function () {
  658. if (xhr.status === 200) {
  659. resolve(JSON.parse(xhr.responseText));
  660. } else {
  661. reject(new Error(xhr.statusText));
  662. }
  663. };
  664. xhr.onerror = function () {
  665. reject(new Error("Network Error"));
  666. };
  667. xhr.send(formData);
  668. });
  669. },
  670. getFileFromPaste(e) {
  671. let clipboardData = e.clipboardData; // IE
  672. if (!clipboardData) {
  673. //chrome
  674. clipboardData = e.originalEvent.clipboardData;
  675. }
  676. let items = "";
  677. items = (e.clipboardData || window.clipboardData).items;
  678. let file = null;
  679. if (!items || items.length === 0) {
  680. this.$message.error(
  681. "当前浏览器不支持粘贴本地图片,请打开图片复制后再粘贴!"
  682. );
  683. return;
  684. }
  685. // 搜索剪切板items
  686. for (let i = 0; i < items.length; i++) {
  687. // 限制上传文件类型
  688. if (items[i].type.indexOf("image") !== -1) {
  689. file = items[i].getAsFile();
  690. break;
  691. }
  692. }
  693. // 对复制黏贴的类型进行判断,若是非文件类型,比如复制黏贴的文字,则不会调用上传文件的函数
  694. if (file) {
  695. return file;
  696. }
  697. },
  698. handlePaste(e) {
  699. let file = this.getFileFromPaste(e);
  700. // 对复制黏贴的类型进行判断,若是非文件类型,比如复制黏贴的文字,则不会调用上传文件的函数
  701. if (file) {
  702. this.uploadFile(this.upload_url, file, {
  703. Authorization: myHeaders["Authorization"],
  704. }).then((res) => {
  705. let fileId = res["id"];
  706. if (fileId) {
  707. let sourceRes = requestUtil.sync(
  708. "https://web_history.tianyunperfect.cn/memos/resourceById",
  709. "post",
  710. { ids: fileId },
  711. {}
  712. );
  713. let sourceRe = sourceRes["res"];
  714. this.tmpFileIds.push(...sourceRe);
  715. }
  716. });
  717. }
  718. },
  719. handlePaste2(e) {
  720. let file = this.getFileFromPaste(e);
  721. // 对复制黏贴的类型进行判断,若是非文件类型,比如复制黏贴的文字,则不会调用上传文件的函数
  722. if (file) {
  723. this.uploadFile(this.upload_url, file, {
  724. Authorization: myHeaders["Authorization"],
  725. }).then((res) => {
  726. let fileId = res["id"];
  727. if (fileId) {
  728. let sourceRes = requestUtil.sync(
  729. "https://web_history.tianyunperfect.cn/memos/resourceById",
  730. "post",
  731. { ids: fileId },
  732. {}
  733. );
  734. let sourceRe = sourceRes["res"];
  735. this.cloneFileIds.push(...sourceRe);
  736. }
  737. });
  738. }
  739. },
  740. keyDown() {
  741. // 如果是ctrl + enter \ meta + enter 则发送
  742. if (event.ctrlKey || event.metaKey) {
  743. this.sendData();
  744. }
  745. },
  746. msg(content) {
  747. this.$message({
  748. message: content,
  749. type: "success",
  750. });
  751. },
  752. isChooseTag(item, tag) {
  753. return item.content.indexOf("#" + tag + " ") !== -1;
  754. },
  755. checkChange(val, item, tag) {
  756. if (val) {
  757. this.addTag(item, tag);
  758. } else {
  759. this.delTag(item, tag);
  760. }
  761. this.resetView(item);
  762. },
  763. addTag(item, cTag) {
  764. let content = item.content;
  765. // 如果已经有了,就不添加
  766. let tmpTag = "#" + cTag + " ";
  767. if (content.indexOf(tmpTag) !== -1) {
  768. return;
  769. }
  770. item.content = tmpTag + content;
  771. // 提交记录
  772. let res = requestUtil.sync(
  773. "https://web_history.tianyunperfect.cn/memos/updateContent",
  774. "post",
  775. {
  776. id: item.id,
  777. content: item.content,
  778. created_ts: item.created_ts,
  779. },
  780. {}
  781. );
  782. if (res.code === 200) {
  783. this.msg("更新成功");
  784. // this.search();
  785. }
  786. },
  787. delTag(item, cTag) {
  788. let content = item.content;
  789. // 如果没有,就不删除
  790. if (content.indexOf(cTag) === -1) {
  791. return;
  792. }
  793. item.content = content.replace("#" + cTag + " ", "");
  794. // 提交记录
  795. let res = requestUtil.sync(
  796. "https://web_history.tianyunperfect.cn/memos/updateContent",
  797. "post",
  798. {
  799. id: item.id,
  800. content: item.content,
  801. created_ts: item.created_ts,
  802. },
  803. {}
  804. );
  805. if (res.code === 200) {
  806. this.msg("更新成功");
  807. // this.search();
  808. }
  809. },
  810. del(item) {
  811. // 提醒是否删除
  812. this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {
  813. confirmButtonText: "确定",
  814. cancelButtonText: "取消",
  815. type: "warning",
  816. }).then(() => {
  817. requestUtil
  818. .async(
  819. `https://web_history.tianyunperfect.cn/memos/delete`,
  820. "post",
  821. { id: item.id },
  822. {}
  823. )
  824. .then((res) => {
  825. if (res.code === 200) {
  826. this.msg("删除成功");
  827. // 在 this.contentList 上直接去除
  828. this.contentList = this.contentList.filter(
  829. (it) => it.id !== item.id
  830. );
  831. }
  832. });
  833. });
  834. },
  835. archive(item) {
  836. requestUtil
  837. .async(
  838. `https://web_history.tianyunperfect.cn/memos/archive`,
  839. "post",
  840. { id: item.id },
  841. {}
  842. )
  843. .then((res) => {
  844. this.msg("归档成功");
  845. // 本地缓存更新
  846. this.contentList = this.contentList.filter(
  847. (it) => it.id !== item.id
  848. );
  849. });
  850. },
  851. handleClose(done) {
  852. this.dialogVisible = false;
  853. done();
  854. },
  855. confirmSubmit(done) {
  856. // 提交记录
  857. let res = requestUtil.sync(
  858. "https://web_history.tianyunperfect.cn/memos/update",
  859. "post",
  860. {
  861. id: this.tmpItem.id,
  862. content: this.tmpItem.content,
  863. created_ts: this.tmpItem.created_ts,
  864. resourceIdList: this.cloneFileIds.map((it) => it.id),
  865. },
  866. {}
  867. );
  868. if (res.code === 200) {
  869. this.msg("更新成功");
  870. this.resetView(this.tmpItem);
  871. // 更新主页面的图片
  872. this.resourceMap[this.tmpItem.id] = this.cloneFileIds;
  873. // this.search();
  874. }
  875. this.dialogVisible = false;
  876. done();
  877. },
  878. edit(item) {
  879. this.tmpItem = item;
  880. this.cloneFileIds = this.getCloneResourceIds(item.id);
  881. this.dialogVisible = true;
  882. // 获取焦点
  883. this.$nextTick(() => {
  884. this.$refs.contentEdit.focus();
  885. });
  886. },
  887. isDisabled(item) {
  888. // 默认不可编辑,这个时候 没有disable属性
  889. return item["disable"] !== false;
  890. },
  891. sendData: function () {
  892. this.sendBtnAble = false;
  893. let content = this.contentStr;
  894. let categories = this.tagChecked;
  895. // 循环拼接类似 #tag #tag2 content
  896. let updatedContent = content;
  897. if (categories.length > 0) {
  898. // 循环拼接类似 #tag #tag2 content
  899. updatedContent =
  900. categories.map((tag) => `#${tag}`).join(" ") + " " + content;
  901. }
  902. // updatedContent 去除尾部的\n,可能有多个
  903. updatedContent = updatedContent.replace(/\n+$/, "");
  904. let res = requestUtil.sync(
  905. "https://memos.tianyunperfect.cn/api/v1/memo",
  906. "post",
  907. {
  908. content: updatedContent,
  909. resourceIdList: this.tmpFileIds.map((it) => it.id),
  910. },
  911. myHeaders
  912. );
  913. if (res["name"]) {
  914. // window.location.href = `https://memos.tianyunperfect.cn/m/${res['name']}`;
  915. this.msg("记录成功");
  916. localStorage.setItem("contentStr", "");
  917. this.contentStr = "";
  918. this.tmpFileIds = [];
  919. this.search();
  920. }
  921. this.sendBtnAble = true;
  922. },
  923. search: function () {
  924. document.documentElement.scrollTo(0, 0);
  925. console.log(this.tagChecked);
  926. let params = {
  927. search: this.searchStr,
  928. tag_str: this.tagChecked.join(","),
  929. page: this.page,
  930. page_size: this.size,
  931. };
  932. let url = "https://web_history.tianyunperfect.cn/memos/list";
  933. let urlWithParams = requestUtil.buildUrl(url, params);
  934. let res = requestUtil.sync(urlWithParams, "get", null, myHeaders);
  935. if (res.code !== 200) {
  936. return;
  937. }
  938. this.contentList = res.res;
  939. this.searchByContent();
  940. },
  941. getAllTags: function () {
  942. this.allTags = requestUtil
  943. .async(
  944. "https://memos.tianyunperfect.cn/api/v1/tag",
  945. "get",
  946. null,
  947. myHeaders
  948. )
  949. .then((res) => {
  950. this.allTags = res;
  951. });
  952. },
  953. confirmSubmit2() {
  954. res = requestUtil.sync(
  955. "https://web_history.tianyunperfect.cn/memos/replace",
  956. "post",
  957. {
  958. old_text: this.old_text,
  959. new_text: this.new_text,
  960. },
  961. {}
  962. );
  963. if (res.code === 200) {
  964. this.msg("替换成功");
  965. this.search();
  966. }
  967. this.dialogVisible2 = false;
  968. },
  969. searchByContent: function () {
  970. this.resourceMap = {};
  971. // contentList 循环获取里面所有的id数组
  972. let idArr = this.contentList.map((item) => item["id"]);
  973. // 获取资源
  974. let ids = idArr.join(",");
  975. if (!ids) {
  976. return;
  977. }
  978. let sourceRes = requestUtil.sync(
  979. "https://web_history.tianyunperfect.cn/memos/resource",
  980. "post",
  981. { ids: ids },
  982. {}
  983. );
  984. // {'code': 200, 'res': [{'memo_id': 1, 'resource_name': 'EfdmvRsodBviDQWRmDiTaV'}, {'memo_id': 4, 'resource_name': '2rY5kmn3AHsy23vK4vSskV'}, {'memo_id': 7, 'resource_name': 'FMhzJXjoTa7fdtbbCBjXnt'}]}
  985. // 循环遍历,以memo_id为key,resource_name数组为value
  986. sourceRes["res"].forEach((item) => {
  987. if (this.resourceMap[item["memo_id"]]) {
  988. this.resourceMap[item["memo_id"]].push(item);
  989. } else {
  990. this.resourceMap[item["memo_id"]] = [item];
  991. }
  992. });
  993. },
  994. },
  995. });
  996. // 如果处于编辑状态,则执行esc取消
  997. window.onkeydown = function (event) {
  998. let edit = vm.dialogVisible;
  999. if (!edit) {
  1000. return;
  1001. }
  1002. // esc
  1003. if (event.keyCode === 27) {
  1004. vm.dialogVisible = false;
  1005. }
  1006. // 如果是meta + enter 则保存
  1007. if (event.metaKey && event.keyCode === 13) {
  1008. vm.confirmSubmit();
  1009. }
  1010. // 如果是ctrl + enter 则保存
  1011. if (event.ctrlKey && event.keyCode === 13) {
  1012. vm.confirmSubmit();
  1013. }
  1014. };
  1015. </script>
  1016. </body>
  1017. </html>