memos.html 34 KB

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