V2EX - 超级增强

楼中楼回复(支持感谢数排序)、自动签到、回复上下文、记录上次阅读位置、使用 SOV2EX 搜索、列表预览内容、点击帖子弹框展示详情、对用户打标签、正文超长自动折叠、划词 base64 解码、一键@所有人,@管理员、操作按钮(感谢、收藏、回复、隐藏)异步请求、支持黑暗模式

当前为 2023-05-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name V2EX - 超级增强
  3. // @namespace http://tampermonkey.net/
  4. // @version 5.1
  5. // @description 楼中楼回复(支持感谢数排序)、自动签到、回复上下文、记录上次阅读位置、使用 SOV2EX 搜索、列表预览内容、点击帖子弹框展示详情、对用户打标签、正文超长自动折叠、划词 base64 解码、一键@所有人,@管理员、操作按钮(感谢、收藏、回复、隐藏)异步请求、支持黑暗模式
  6. // @author Zyronon
  7. // @match https://*.v2ex.com/
  8. // @match https://*.v2ex.com/?tab=*
  9. // @match https://*.v2ex.com/t/*
  10. // @match https://*.v2ex.com/recent*
  11. // @match https://*.v2ex.com/go/*
  12. // @icon https://www.google.com/s2/favicons?sz=64&domain=v2ex.com
  13. // @require https://cdn.jsdelivr.net/npm/vue@3.2.47/dist/vue.global.prod.js
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_unregisterMenuCommand
  16. // @grant GM_openInTab
  17. // @grant GM_notification
  18. // @license GPL License
  19. // ==/UserScript==
  20.  
  21. (t=>{const e=document.createElement("style");e.dataset.source="vite-plugin-monkey",e.textContent=t,document.head.append(e)})(' .v-enter-active,.v-leave-active{transition:opacity .3s ease}.v-enter-from,.v-leave-to{opacity:0}.username{font-weight:700;font-size:1.4rem;margin-right:1rem}.op{display:inline-block;background-color:transparent;color:#1484cd;border-radius:.3rem;padding:0 .3rem;cursor:default;border:2px solid #1484cd;font-size:1.2rem;font-weight:700;margin-right:1rem;transform:scale(.8)}.mod{display:inline-block;background-color:transparent;color:#1484cd;border-radius:.3rem;padding:0 .3rem;cursor:default;border:2px solid #1484cd;font-size:1.2rem;font-weight:700;transform:scale(.8);background:#1484cd;color:#fff;margin-right:1rem}.my-tag{font-size:1.4rem;color:red;margin-left:1rem}.my-tag:hover .remove{display:inline}.my-tag .remove{cursor:pointer;margin-left:.5rem;display:none}.floor{margin-left:1rem;font-size:1.2rem;line-height:1rem;border-radius:1rem;display:inline-block;background-color:#f0f0f0;color:#ccc;padding:.2rem .5rem;cursor:default}html,body{font-size:62.5%}.flex{display:flex;align-items:center;justify-content:space-between}.flex-end{justify-content:flex-end}.flex-center{justify-content:center}.p1{padding:1rem}.p0{padding:0!important}.post-author{display:flex;align-items:center;position:relative;color:#ccc!important}.post-author>.username{font-size:1.2rem}.sticky{position:sticky;bottom:0}.sticky[stuck]{box-shadow:0 2px 20px #00000059}a{color:#778087;text-decoration:none;cursor:pointer}a:hover{text-decoration:underline}.base-loading{border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle infinite 1s linear}.loading-c{border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle infinite 1s linear;width:3rem;height:3rem}.loading-b{border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle infinite 1s linear;border-color:#ffffff rgba(178,177,177,.2) rgba(178,177,177,.2) rgba(178,177,177,.2);width:3rem;height:3rem}@keyframes circle{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.button{cursor:pointer;padding:.4rem 2.4rem;border-radius:5px;display:inline-flex;justify-content:center;align-items:center;font-weight:700;font-size:1.2rem;color:#fff;background:#40a9ff;border:1px solid #40a9ff;user-select:none}.button:hover{opacity:.9}.button.info{color:#000;border:1px solid #40a9ff;background:white}.button.gray{color:#f5f5f5;border:1px solid #b6b6b6;background:#b6b6b6}.button.light{color:gray;border:1px solid #e2e2e2;background:#e2e2e2}.button:before{content:" ";border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle infinite 1s linear;border-color:#fff transparent transparent transparent;width:1rem;height:1rem;margin-right:1rem;display:none}.button.loading{cursor:not-allowed;opacity:.5}.button.loading:before{display:block}.button.disabled{cursor:not-allowed;color:#c6c6c6;background:#8d8d8d;border:1px solid transparent}.button.isNight{color:#c6c6c6;background:#2b4054;border:1px solid transparent}.tool{position:relative;margin-left:1rem;display:flex;align-items:center;font-size:1.2rem;font-weight:700;border-radius:.2rem;cursor:pointer;height:3rem;padding:0 .5rem}.tool:before{content:" ";border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle infinite 1s linear;border-color:transparent #929596 #929596 #929596;width:1rem;height:1rem;margin-left:1rem;display:none}.tool.loading{cursor:not-allowed;opacity:.5}.tool.loading:before{display:block}.tool.loading:hover{background:unset}.tool>svg{width:1.6rem!important;height:1.6rem!important;margin-right:.4rem;box-sizing:border-box;border-radius:.2rem}.tool:hover{background:#e8e8e8}.tool.no-hover{cursor:default}.tool.no-hover:hover{background:unset}.my-node{border-radius:.2rem;padding:.4rem;font-size:1rem;color:#999;background:#f5f5f5;cursor:pointer}.my-node:hover{text-decoration:none;background:#e2e2e2}.msgs{position:fixed;margin-left:calc(50% - 25rem);width:50rem;z-index:9999;bottom:0;left:0;right:0}.msg{cursor:default;margin-bottom:2rem;background:white;display:flex;color:#000;font-size:1.4rem;box-sizing:border-box;border-radius:.4rem;box-shadow:0 0 1rem 1px silver}.msg.success .left{background:#40a9ff}.msg.warning .left{background:#c8c002}.msg.error .left{background:red}.msg .left{border-radius:.4rem 0 0 .4rem;display:flex;align-items:center;background:#40a9ff}.msg .left svg{margin:0 .3rem;cursor:pointer}.msg .right{flex:1;padding:1rem 2rem;display:flex;justify-content:space-between;align-items:center}.line{border-bottom:1px solid #e2e2e2}.my-box{box-shadow:0 2px 3px #0000001a;border-radius:.4rem;background:white;margin-bottom:2rem;width:100%;overflow:hidden;box-sizing:border-box}.my-cell{padding:1rem;font-size:1.4rem;line-height:150%;text-align:left;border-bottom:1px solid #e2e2e2}.f14{font-size:1.4rem}.switch{width:4.5rem;height:2rem;border-radius:2rem;position:relative;display:flex;align-items:center;transition:all .3s}.switch.light{border:1px solid #e2e2e2}.switch.light.active{background:#e2e2e2}.switch.light.active:before{right:.2rem;background:white}.switch.light:before{background:#e2e2e2}.switch.gray{border:1px solid #ccc}.switch.gray.active{background:#ccc}.switch.gray.active:before{right:.2rem;background:white}.switch.gray:before{background:#ccc}.switch.isNight{border:1px solid #31475e}.switch.isNight.active{background:#31475e}.switch.isNight.active:before{right:.2rem;background:gray}.switch.isNight:before{background:#31475e}.switch:before{position:absolute;content:" ";transition:all .3s;right:calc(100% - 2rem);width:1.8rem;height:1.8rem;border-radius:50%}.modal{position:fixed;z-index:100;width:100vw;height:100vh;left:0;top:0;display:flex;justify-content:center;align-items:center}.modal .title{font-size:2.4rem;margin-bottom:1rem;text-align:center}.modal .option{display:flex;justify-content:space-between;align-items:center;padding:.6rem 0}.modal .mask{position:fixed;width:100vw;height:100vh;left:0;top:0;background:rgba(0,0,0,.3)}.radio-group2{display:inline-flex;border-radius:.5rem;overflow:hidden;border:1px solid #e2e2e2}.radio-group2 .radio{cursor:pointer;background:transparent;padding:.4rem 1rem;border-left:1px solid #e2e2e2;color:#9ca1a4;font-size:1.2rem}.radio-group2 .radio:first-child{border-left:none}.radio-group2 .active{background:#e2e2e2;color:gray}.radio-group2.isNight{border:1px solid #454847}.radio-group2.isNight .radio{border-left:1px solid #454847;color:#fff}.radio-group2.isNight .active{background:#31475e}.pop-confirm{position:relative;display:inline-flex;justify-content:center}.tip{position:fixed;background:black;color:#fff;max-width:10rem;padding:.7rem 1rem;border-radius:.5rem;transform:translate(-50%);z-index:999}input{height:3rem;outline:unset;border:1px solid #e1e1e1;padding:0 .5rem;border-radius:5px;box-sizing:border-box}.isNight .wrapper[data-v-101664b8]{background:#22303f!important}.isNight .wrapper .title[data-v-101664b8],.isNight .wrapper .option-title[data-v-101664b8],.isNight .wrapper .option>span[data-v-101664b8]{color:#a9a9a9}.isNight .wrapper .notice[data-v-101664b8]{color:gray!important}.setting-modal .wrapper[data-v-101664b8]{z-index:9;background:#f1f1f1;border-radius:.8rem;font-size:1.4rem;padding:2rem 6rem 4rem}.setting-modal .wrapper .sub-title[data-v-101664b8]{color:gray;font-size:1.4rem;margin-bottom:4rem}.setting-modal .wrapper .option-title[data-v-101664b8]{text-align:start;font-size:1.6rem;font-weight:700;margin-top:1.5rem}.setting-modal .wrapper .body[data-v-101664b8]{display:flex;gap:10rem}.setting-modal .wrapper .body .option-list[data-v-101664b8]{width:45rem}.setting-modal .wrapper .notice[data-v-101664b8]{font-size:12px;padding-left:3rem;text-align:left}.setting-modal .wrapper .notice a[data-v-101664b8]{color:#00f}.setting-modal .wrapper .jieshao[data-v-101664b8]{margin-top:2rem;font-size:15px;font-weight:700;color:red;display:flex;justify-content:flex-start;line-break:anywhere;text-align:left}.pop-confirm-content[data-v-8df5d12b]{position:fixed;background:white;padding:1.5rem;box-shadow:0 0 12px #0003;border-radius:.4rem;transform:translate(-50%,calc(-100% - 1rem));z-index:999}.pop-confirm-content .text[data-v-8df5d12b]{color:#000;text-align:start;font-size:1.4rem;width:15rem;min-width:15rem}.pop-confirm-content .options[data-v-8df5d12b]{margin-top:1.5rem;display:flex;justify-content:flex-end;align-items:center;gap:1rem;font-size:1rem}.pop-confirm-content .options div[data-v-8df5d12b]{cursor:pointer}.pop-confirm-content .options .main[data-v-8df5d12b]{color:gray;background:#e2e2e2;padding:.3rem .8rem;border-radius:.2rem}.point[data-v-810a119b]{margin-left:1rem;font-size:1.2rem;min-width:4rem;border-radius:.4rem 0 0 .4rem;display:flex;align-items:center;flex-direction:row!important;padding:0!important}.point .up[data-v-810a119b]{display:flex;flex-direction:column;align-items:center;justify-content:center}.point .num[data-v-810a119b]{margin-left:.2rem;font-weight:700;color:#000;user-select:none}.point svg[data-v-810a119b]{width:2rem;padding:.4rem;border-radius:.2rem}.point svg[data-v-810a119b]:hover{background:#e5e5e5}.point svg.disabled[data-v-810a119b]{cursor:not-allowed}.point svg.disabled[data-v-810a119b]:hover{background:unset!important}.Author[data-v-d8c00e09]{display:flex;align-items:center;justify-content:space-between;font-size:1.2rem;position:relative;margin-bottom:.4rem}.Author.expand[data-v-d8c00e09]{margin-bottom:0}.Author .Author-left[data-v-d8c00e09]{display:flex;align-items:center;max-width:90%}.Author .Author-left .username[data-v-d8c00e09]{font-size:1.4rem;margin-right:1rem}.Author .Author-left .expand-icon[data-v-d8c00e09]{cursor:pointer;margin-right:.8rem;width:2rem;height:2rem;transform:rotate(90deg)}.Author .Author-left .avatar[data-v-d8c00e09]{margin-right:1rem;display:flex}.Author .Author-left .avatar img[data-v-d8c00e09]{width:3.4rem;height:3.4rem;border-radius:.3rem}.Author .Author-left .texts[data-v-d8c00e09]{flex:1}.Author .Author-left .op[data-v-d8c00e09]{display:inline-block;background-color:transparent;color:#1484cd;border-radius:.3rem;padding:0 .3rem;cursor:default;border:2px solid #1484cd;font-size:1.2rem;font-weight:700;margin-right:1rem;transform:scale(.8)}.Author .Author-left .mod[data-v-d8c00e09]{display:inline-block;background-color:transparent;color:#1484cd;border-radius:.3rem;padding:0 .3rem;cursor:default;border:2px solid #1484cd;font-size:1.2rem;font-weight:700;transform:scale(.8);background:#1484cd;color:#fff;margin-right:1rem}.Author .Author-left .my-tag[data-v-d8c00e09]{font-size:1.4rem;color:red;margin-left:1rem}.Author .Author-left .my-tag:hover .remove[data-v-d8c00e09]{display:inline}.Author .Author-left .my-tag .remove[data-v-d8c00e09]{cursor:pointer;margin-left:.5rem;display:none}.Author .Author-left .add-tag[data-v-d8c00e09]{font-size:2.5rem;transform:translateY(.2rem);line-height:1rem;display:inline-block;margin-left:1rem;cursor:pointer;display:none}.Author:hover .add-tag[data-v-d8c00e09]{display:inline-block}.Author .Author-right[data-v-d8c00e09]{position:absolute;right:0;display:flex;align-items:center}.Author .Author-right .toolbar[data-v-d8c00e09]{display:flex;align-items:center;color:#929596;opacity:0}.Author .Author-right .toolbar[data-v-d8c00e09]:hover{background:white;opacity:1}.Author .Author-right .floor[data-v-d8c00e09]{margin-left:1rem;font-size:1.2rem;line-height:1rem;border-radius:1rem;display:inline-block;background-color:#f0f0f0;color:#ccc;padding:.2rem .5rem;cursor:default}.Author .Author-right .isDev[data-v-d8c00e09]{color:#000!important}.post-editor-wrapper[data-v-0cd7749c]{width:100%;box-sizing:border-box;position:relative;overflow:hidden;transition:all .3s}.post-editor-wrapper.reply-post .post-editor[data-v-0cd7749c]{border:1px solid #e2e2e2;border-radius:.4rem}.post-editor-wrapper.reply-post.isFocus .post-editor[data-v-0cd7749c]{border:1px solid #968b8b}.post-editor-wrapper.reply-comment[data-v-0cd7749c]{border:1px solid #e2e2e2;border-radius:.4rem;overflow:hidden}.post-editor-wrapper.reply-comment.isFocus[data-v-0cd7749c]{border:1px solid #968b8b}.post-editor-wrapper.reply-comment .toolbar[data-v-0cd7749c]{background:#f6f7f8}.post-editor-wrapper .post-editor[data-v-0cd7749c]{transition:border .3s;width:100%;max-width:100%;padding:.6rem 1.4rem;box-sizing:border-box;border:none;outline:none;font-family:Avenir,Helvetica,Arial,sans-serif;font-size:1.4rem;min-height:13rem;resize:none}.post-editor-wrapper .toolbar[data-v-0cd7749c]{box-sizing:border-box;padding:.5rem 1rem;width:100%;position:relative;display:flex;justify-content:space-between;align-items:center}.post-editor-wrapper .toolbar span[data-v-0cd7749c]{color:gray;font-size:1.3rem}.post-editor-wrapper .get-cursor[data-v-0cd7749c]{transition:border .3s;width:100%;max-width:100%;padding:.6rem 1.4rem;box-sizing:border-box;border:none;outline:none;font-family:Avenir,Helvetica,Arial,sans-serif;font-size:1.4rem;min-height:13rem;resize:none;position:absolute;top:0;z-index:-100}.v-enter-active[data-v-a77c3d96],.v-leave-active[data-v-a77c3d96]{transition:opacity .3s ease}.v-enter-from[data-v-a77c3d96],.v-leave-to[data-v-a77c3d96]{opacity:0}.username[data-v-a77c3d96]{font-weight:700;font-size:1.4rem;margin-right:1rem}.op[data-v-a77c3d96]{display:inline-block;background-color:transparent;color:#1484cd;border-radius:.3rem;padding:0 .3rem;cursor:default;border:2px solid #1484cd;font-size:1.2rem;font-weight:700;margin-right:1rem;transform:scale(.8)}.mod[data-v-a77c3d96]{display:inline-block;background-color:transparent;color:#1484cd;border-radius:.3rem;padding:0 .3rem;cursor:default;border:2px solid #1484cd;font-size:1.2rem;font-weight:700;transform:scale(.8);background:#1484cd;color:#fff;margin-right:1rem}.my-tag[data-v-a77c3d96]{font-size:1.4rem;color:red;margin-left:1rem}.my-tag:hover .remove[data-v-a77c3d96]{display:inline}.my-tag .remove[data-v-a77c3d96]{cursor:pointer;margin-left:.5rem;display:none}.floor[data-v-a77c3d96]{margin-left:1rem;font-size:1.2rem;line-height:1rem;border-radius:1rem;display:inline-block;background-color:#f0f0f0;color:#ccc;padding:.2rem .5rem;cursor:default}html[data-v-a77c3d96],body[data-v-a77c3d96]{font-size:62.5%}.flex[data-v-a77c3d96]{display:flex;align-items:center;justify-content:space-between}.flex-end[data-v-a77c3d96]{justify-content:flex-end}.flex-center[data-v-a77c3d96]{justify-content:center}.p1[data-v-a77c3d96]{padding:1rem}.p0[data-v-a77c3d96]{padding:0!important}.post-author[data-v-a77c3d96]{display:flex;align-items:center;position:relative;color:#ccc!important}.post-author>.username[data-v-a77c3d96]{font-size:1.2rem}.sticky[data-v-a77c3d96]{position:sticky;bottom:0}.sticky[stuck][data-v-a77c3d96]{box-shadow:0 2px 20px #00000059}a[data-v-a77c3d96]{color:#778087;text-decoration:none;cursor:pointer}a[data-v-a77c3d96]:hover{text-decoration:underline}.base-loading[data-v-a77c3d96]{border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle-a77c3d96 infinite 1s linear}.loading-c[data-v-a77c3d96]{border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle-a77c3d96 infinite 1s linear;width:3rem;height:3rem}.loading-b[data-v-a77c3d96]{border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle-a77c3d96 infinite 1s linear;border-color:#ffffff rgba(178,177,177,.2) rgba(178,177,177,.2) rgba(178,177,177,.2);width:3rem;height:3rem}@keyframes circle-a77c3d96{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.button[data-v-a77c3d96]{cursor:pointer;padding:.4rem 2.4rem;border-radius:5px;display:inline-flex;justify-content:center;align-items:center;font-weight:700;font-size:1.2rem;color:#fff;background:#40a9ff;border:1px solid #40a9ff;user-select:none}.button[data-v-a77c3d96]:hover{opacity:.9}.button.info[data-v-a77c3d96]{color:#000;border:1px solid #40a9ff;background:white}.button.gray[data-v-a77c3d96]{color:#f5f5f5;border:1px solid #b6b6b6;background:#b6b6b6}.button.light[data-v-a77c3d96]{color:gray;border:1px solid #e2e2e2;background:#e2e2e2}.button[data-v-a77c3d96]:before{content:" ";border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle-a77c3d96 infinite 1s linear;border-color:#fff transparent transparent transparent;width:1rem;height:1rem;margin-right:1rem;display:none}.button.loading[data-v-a77c3d96]{cursor:not-allowed;opacity:.5}.button.loading[data-v-a77c3d96]:before{display:block}.button.disabled[data-v-a77c3d96]{cursor:not-allowed;color:#c6c6c6;background:#8d8d8d;border:1px solid transparent}.button.isNight[data-v-a77c3d96]{color:#c6c6c6;background:#2b4054;border:1px solid transparent}.tool[data-v-a77c3d96]{position:relative;margin-left:1rem;display:flex;align-items:center;font-size:1.2rem;font-weight:700;border-radius:.2rem;cursor:pointer;height:3rem;padding:0 .5rem}.tool[data-v-a77c3d96]:before{content:" ";border:2px solid;border-color:#000 #00000033 #00000033 #00000033;border-radius:100%;animation:circle-a77c3d96 infinite 1s linear;border-color:transparent #929596 #929596 #929596;width:1rem;height:1rem;margin-left:1rem;display:none}.tool.loading[data-v-a77c3d96]{cursor:not-allowed;opacity:.5}.tool.loading[data-v-a77c3d96]:before{display:block}.tool.loading[data-v-a77c3d96]:hover{background:unset}.tool>svg[data-v-a77c3d96]{width:1.6rem!important;height:1.6rem!important;margin-right:.4rem;box-sizing:border-box;border-radius:.2rem}.tool[data-v-a77c3d96]:hover{background:#e8e8e8}.tool.no-hover[data-v-a77c3d96]{cursor:default}.tool.no-hover[data-v-a77c3d96]:hover{background:unset}.my-node[data-v-a77c3d96]{border-radius:.2rem;padding:.4rem;font-size:1rem;color:#999;background:#f5f5f5;cursor:pointer}.my-node[data-v-a77c3d96]:hover{text-decoration:none;background:#e2e2e2}.msgs[data-v-a77c3d96]{position:fixed;margin-left:calc(50% - 25rem);width:50rem;z-index:9999;bottom:0;left:0;right:0}.msg[data-v-a77c3d96]{cursor:default;margin-bottom:2rem;background:white;display:flex;color:#000;font-size:1.4rem;box-sizing:border-box;border-radius:.4rem;box-shadow:0 0 1rem 1px silver}.msg.success .left[data-v-a77c3d96]{background:#40a9ff}.msg.warning .left[data-v-a77c3d96]{background:#c8c002}.msg.error .left[data-v-a77c3d96]{background:red}.msg .left[data-v-a77c3d96]{border-radius:.4rem 0 0 .4rem;display:flex;align-items:center;background:#40a9ff}.msg .left svg[data-v-a77c3d96]{margin:0 .3rem;cursor:pointer}.msg .right[data-v-a77c3d96]{flex:1;padding:1rem 2rem;display:flex;justify-content:space-between;align-items:center}.line[data-v-a77c3d96]{border-bottom:1px solid #e2e2e2}.my-box[data-v-a77c3d96]{box-shadow:0 2px 3px #0000001a;border-radius:.4rem;background:white;margin-bottom:2rem;width:100%;overflow:hidden;box-sizing:border-box}.my-cell[data-v-a77c3d96]{padding:1rem;font-size:1.4rem;line-height:150%;text-align:left;border-bottom:1px solid #e2e2e2}.f14[data-v-a77c3d96]{font-size:1.4rem}.switch[data-v-a77c3d96]{width:4.5rem;height:2rem;border-radius:2rem;position:relative;display:flex;align-items:center;transition:all .3s}.switch.light[data-v-a77c3d96]{border:1px solid #e2e2e2}.switch.light.active[data-v-a77c3d96]{background:#e2e2e2}.switch.light.active[data-v-a77c3d96]:before{right:.2rem;background:white}.switch.light[data-v-a77c3d96]:before{background:#e2e2e2}.switch.gray[data-v-a77c3d96]{border:1px solid #ccc}.switch.gray.active[data-v-a77c3d96]{background:#ccc}.switch.gray.active[data-v-a77c3d96]:before{right:.2rem;background:white}.switch.gray[data-v-a77c3d96]:before{background:#ccc}.switch.isNight[data-v-a77c3d96]{border:1px solid #31475e}.switch.isNight.active[data-v-a77c3d96]{background:#31475e}.switch.isNight.active[data-v-a77c3d96]:before{right:.2rem;background:gray}.switch.isNight[data-v-a77c3d96]:before{background:#31475e}.switch[data-v-a77c3d96]:before{position:absolute;content:" ";transition:all .3s;right:calc(100% - 2rem);width:1.8rem;height:1.8rem;border-radius:50%}.modal[data-v-a77c3d96]{position:fixed;z-index:100;width:100vw;height:100vh;left:0;top:0;display:flex;justify-content:center;align-items:center}.modal .title[data-v-a77c3d96]{font-size:2.4rem;margin-bottom:1rem;text-align:center}.modal .option[data-v-a77c3d96]{display:flex;justify-content:space-between;align-items:center;padding:.6rem 0}.modal .mask[data-v-a77c3d96]{position:fixed;width:100vw;height:100vh;left:0;top:0;background:rgba(0,0,0,.3)}.radio-group2[data-v-a77c3d96]{display:inline-flex;border-radius:.5rem;overflow:hidden;border:1px solid #e2e2e2}.radio-group2 .radio[data-v-a77c3d96]{cursor:pointer;background:transparent;padding:.4rem 1rem;border-left:1px solid #e2e2e2;color:#9ca1a4;font-size:1.2rem}.radio-group2 .radio[data-v-a77c3d96]:first-child{border-left:none}.radio-group2 .active[data-v-a77c3d96]{background:#e2e2e2;color:gray}.radio-group2.isNight[data-v-a77c3d96]{border:1px solid #454847}.radio-group2.isNight .radio[data-v-a77c3d96]{border-left:1px solid #454847;color:#fff}.radio-group2.isNight .active[data-v-a77c3d96]{background:#31475e}.pop-confirm[data-v-a77c3d96]{position:relative;display:inline-flex;justify-content:center}.tip[data-v-a77c3d96]{position:fixed;background:black;color:#fff;max-width:10rem;padding:.7rem 1rem;border-radius:.5rem;transform:translate(-50%);z-index:999}input[data-v-a77c3d96]{height:3rem;outline:unset;border:1px solid #e1e1e1;padding:0 .5rem;border-radius:5px;box-sizing:border-box}.html-wrapper[data-v-a77c3d96]{position:relative}.html-wrapper .mask[data-v-a77c3d96]{max-height:90rem;overflow:hidden;-webkit-mask-image:linear-gradient(180deg,#000 80%,transparent)}.html-wrapper .expand[data-v-a77c3d96]{position:absolute;z-index:1;bottom:2rem;padding:.2rem 1.5rem;border-radius:2rem;border:1px solid gray;background:white;color:gray;left:50%;transform:translate(-50%);cursor:pointer}.comment[data-v-cb22281c]{width:100%;box-sizing:border-box;margin-top:1rem}.comment.isLevelOne[data-v-cb22281c]{border-bottom:1px solid #ececec;padding:1rem;margin-top:0}.comment.ding[data-v-cb22281c]{background:rgba(255,255,0,.3)!important}.comment .comment-content-w .more[data-v-cb22281c]{text-align:center;margin:2rem 0}.comment .comment-content[data-v-cb22281c]{display:flex;position:relative}.comment .comment-content .expand-line[data-v-cb22281c]{cursor:pointer;width:3rem;min-width:3rem;position:relative}.comment .comment-content .expand-line[data-v-cb22281c]:after{position:absolute;left:calc(60% - 1px);content:" ";height:100%;width:0;border-right:1px solid #ececec}.comment .comment-content .expand-line[data-v-cb22281c]:hover:after{border-right:2px solid #0079D3}.comment .comment-content .right[data-v-cb22281c]{flex:1;width:calc(100% - 3rem)}.comment .comment-content .right .w[data-v-cb22281c]{padding-left:1.7rem}.comment .comment-content .right .w .post-editor-wrapper[data-v-cb22281c]{margin-top:1rem}.wrong-wrapper[data-v-cb22281c]{font-size:1.4rem;margin-bottom:1rem}.wrong-wrapper span[data-v-cb22281c]{cursor:pointer}.wrong-wrapper .del-line[data-v-cb22281c]{text-decoration:line-through}.wrong-wrapper .wrong-icon[data-v-cb22281c]{margin-left:.5rem}.wrong-wrapper .warning[data-v-cb22281c]{border-top:1px solid #e1e1e1;border-bottom:1px solid #e1e1e1;padding:1rem 0;margin-top:1rem;font-size:1.2rem;color:red}.toolbar[data-v-0f5085fc]{display:flex;align-items:center;color:#929596}.comment[data-v-3eb530b9]{width:100%;box-sizing:border-box;display:flex;gap:1rem;padding:1rem;border-bottom:1px solid #e2e2e2}.comment .avatar[data-v-3eb530b9]{display:flex}.comment .avatar img[data-v-3eb530b9]{width:3.8rem;height:3.8rem;border-radius:.3rem}.comment .comment-body[data-v-3eb530b9]{flex:1;display:flex;flex-direction:column}.comment .comment-body .texts[data-v-3eb530b9]{display:flex;align-items:center}.comment .comment-body .reply_content[data-v-3eb530b9]{margin-top:1rem;max-width:calc(100% - 5rem)}.comment .isRight[data-v-3eb530b9]{align-items:flex-end}.comment .isRight .op[data-v-3eb530b9],.comment .isRight .mod[data-v-3eb530b9],.comment .isRight .username[data-v-3eb530b9]{margin:0 0 0 1rem}.comment .Author-right[data-v-3eb530b9]{display:flex;flex-direction:column;align-items:center}.comment .Author-right .floor[data-v-3eb530b9]{margin-left:0}.comment .Author-right .jump[data-v-3eb530b9]{color:#929596;margin-left:0}.comment .point[data-v-3eb530b9]{margin:0 .5rem;font-size:1.4rem;display:flex;gap:.5rem;align-items:center;font-weight:700;color:#000}.sticky{position:sticky;bottom:-2px;z-index:2}.sticky[stuck]{box-shadow:0 2px 20px #00000059!important}.Post[data-v-13ba59da]{position:unset!important;background:transparent!important;overflow:unset!important}.Post .main[data-v-13ba59da]{background:transparent!important;padding:unset!important;width:100%!important}.Post .close-btn[data-v-13ba59da]{display:none}.post-detail[data-v-13ba59da]{text-align:start;position:fixed;z-index:99;left:0;right:0;bottom:0;top:0;background:rgba(46,47,48,.8);overflow:auto;font-size:1.4rem;display:flex;justify-content:center;flex-wrap:wrap}.post-detail.isNight[data-v-13ba59da]{background:rgba(46,47,48,.8)}.post-detail.isNight .main[data-v-13ba59da]{background:#22303f}.post-detail.isNight .main .toolbar-wrapper[data-v-13ba59da]{border-top:unset!important}.post-detail.isNight .main .button.gray[data-v-13ba59da]{background:#18222d!important;border:1px solid #18222d!important}.post-detail.isNight .main .relationReply[data-v-13ba59da]{color:#fff}.post-detail.isNight .main .relationReply .comments[data-v-13ba59da],.post-detail.isNight .main .relationReply .my-cell[data-v-13ba59da]{background:#18222d}.post-detail.isNight .main .relationReply .comment[data-v-13ba59da]{border-bottom:1px solid #22303f}.post-detail.isNight .main .my-box[data-v-13ba59da]{color:#fff;background:#18222d}.post-detail.isNight .main .my-box .title[data-v-13ba59da],.post-detail.isNight .main .my-box .content[data-v-13ba59da]{color:#d1d5d9!important}.post-detail.isNight .main .my-box .base-info[data-v-13ba59da],.post-detail.isNight .main .my-box .content[data-v-13ba59da]{border:1px solid #22303f!important}.post-detail.isNight .main[data-v-13ba59da] .subtle .fade{color:#b2c3d4!important}.post-detail.isNight .main[data-v-13ba59da] .subtle .topic_content{color:#d1d5d9!important}.post-detail.isNight .main .my-cell[data-v-13ba59da]{border-bottom:1px solid #22303f!important}.post-detail.isNight .main[data-v-13ba59da] .isLevelOne{border-bottom:1px solid #22303f}.post-detail.isNight .main[data-v-13ba59da] .comment .expand-line:after{border-right:1px solid #22303f!important}.post-detail.isNight .main[data-v-13ba59da] .comment .expand-line:hover:after{border-right:2px solid #0079D3!important}.post-detail.isNight .main[data-v-13ba59da] .comment .comment-content .w>.text{color:#d1d5d9!important}.post-detail.isNight .main[data-v-13ba59da] .Author-right .toolbar:hover{background:#18222d!important}.post-detail.isNight .main[data-v-13ba59da] .Author-right .tool{background:#22303f!important}.post-detail.isNight .main[data-v-13ba59da] .point svg:hover{background:#22303f}.post-detail.isNight .main[data-v-13ba59da] .point .num{color:#d1d5d9!important}.post-detail.isNight .main[data-v-13ba59da] .floor{background:#393f4e!important;color:#d1d5d9!important}.post-detail.isNight .main .editor-wrapper[data-v-13ba59da]{background:#393f4e!important}.post-detail.isNight .main[data-v-13ba59da] .post-editor-wrapper .post-editor{background:#18222d;border:transparent;color:#fff}.post-detail.isNight .main[data-v-13ba59da] .post-editor-wrapper .toolbar{background:#393f4e!important}.post-detail.isNight .main .call-list[data-v-13ba59da]{background:#22303f}.post-detail.isNight .main .call-list .call-item[data-v-13ba59da]{border-top:1px solid #18222d}.post-detail.isNight .main .call-list .call-item .select[data-v-13ba59da],.post-detail.isNight .main .call-list .call-item[data-v-13ba59da]:hover,.post-detail.isNight .main .call-list .call-item.select[data-v-13ba59da]{background-color:#393f4e;text-decoration:none}.post-detail.isNight .main .scroll-to[data-v-13ba59da],.post-detail.isNight .main .close-btn[data-v-13ba59da],.post-detail.isNight .main .scroll-top[data-v-13ba59da],.post-detail.isNight .main .top-reply[data-v-13ba59da]{color:#9caec7}.post-detail .main[data-v-13ba59da]{display:flex;justify-content:flex-end;padding:3rem 8rem 15rem;background:#e2e2e2;position:relative}.post-detail .main .main-wrapper[data-v-13ba59da]{width:77rem;padding-bottom:2rem;display:flex;flex-direction:column;align-items:center;position:relative}.post-detail .main .main-wrapper .post-wrapper .toolbar-wrapper[data-v-13ba59da]{border-top:1px solid #e2e2e2;height:4rem;padding-left:.6rem;display:flex;align-items:center}.post-detail .main .main-wrapper .editor-wrapper .float[data-v-13ba59da]{margin-right:2rem}.post-detail .main .main-wrapper .editor-wrapper .w[data-v-13ba59da]{padding:1.2rem}.post-detail .main .main-wrapper .comment-wrapper .comments[data-v-13ba59da]{width:100%;box-sizing:border-box}.post-detail .main .main-wrapper .loading-wrapper[data-v-13ba59da]{height:20rem;display:flex;justify-content:center;align-items:center}.post-detail .main .main-wrapper #no-comments-yet[data-v-13ba59da]{color:#a9a9a9;font-weight:700;text-align:center;width:100%;margin-bottom:2rem;box-sizing:border-box}.post-detail .main .relationReply[data-v-13ba59da]{position:fixed;width:25vw;top:6.5rem;bottom:15rem;z-index:99;transform:translate(calc(100% + 2rem));font-size:2rem;overflow:hidden}.post-detail .main .relationReply .my-cell[data-v-13ba59da]{background:white;border-radius:.5rem .5rem 0 0}.post-detail .main .relationReply .comments[data-v-13ba59da]{max-height:calc(100% - 4.2rem);overflow:auto;background:white;border-radius:0 0 .5rem .5rem}.post-detail .main .call-list[data-v-13ba59da]{z-index:9;position:absolute;top:12rem;border:1px solid #ccc;background-color:#fff;box-shadow:0 5px 15px #0000001a;overflow:hidden;max-height:30rem;min-width:8rem;box-sizing:content-box}.post-detail .main .call-list .call-item[data-v-13ba59da]{border-top:1px solid #ccc;height:3rem;display:flex;padding:0 1rem;align-items:center;cursor:pointer;font-size:14px;box-sizing:border-box}.post-detail .main .call-list .call-item .select[data-v-13ba59da],.post-detail .main .call-list .call-item[data-v-13ba59da]:hover,.post-detail .main .call-list .call-item.select[data-v-13ba59da]{background-color:#f0f0f0;text-decoration:none}.post-detail .main .call-list .call-item[data-v-13ba59da]:nth-child(1){border-top:1px solid transparent}@media screen and (max-width: 1500px){.post-detail .main-wrapper[data-v-13ba59da]{width:65vw!important}}@media screen and (max-width: 1280px){.post-detail .main-wrapper[data-v-13ba59da]{width:75vw!important}}.post-detail .scroll-top[data-v-13ba59da]{position:fixed;bottom:10rem;z-index:99;padding:.6rem .2rem;width:3.5rem;transform:translate(6rem);font-size:2rem;background:#f1f1f1;border:none;color:#a9a9a9}.post-detail .scroll-to[data-v-13ba59da]{position:fixed;bottom:10rem;z-index:99;padding:.6rem .2rem;width:3.5rem;transform:translate(6rem);font-size:2rem;background:#f1f1f1;border:none;color:#a9a9a9;bottom:15rem;display:flex;flex-direction:column}.post-detail .scroll-to input[data-v-13ba59da]{margin-top:.5rem;height:2rem;width:3.3rem;font-size:1.4rem;text-align:center;color:gray}.post-detail .read-notice[data-v-13ba59da]{display:flex;align-items:center;color:gray}.post-detail .read-notice .jump[data-v-13ba59da]{background:#f1f1f1;color:gray;padding:.3rem 1rem;border-radius:.4rem;margin:0 1rem;cursor:pointer}.post-detail .close-btn[data-v-13ba59da]{color:#b6b6b6;cursor:pointer;position:fixed;top:3rem;transform:translate(4rem);font-size:2rem}.post-detail .top-reply[data-v-13ba59da]{color:#e2e2e2;cursor:pointer;font-size:2rem;display:flex}.post-detail .top-reply i[data-v-13ba59da]{padding:0 1rem}.isNight[data-v-19fe372d]{background:#22303f!important;color:#ccc!important}.base64_tooltip[data-v-19fe372d]{box-shadow:0 3px 6px -4px #0000001f,0 6px 16px #00000014,0 9px 28px 8px #0000000d;background:white;min-height:2.2rem;max-width:20rem;padding:.8rem;position:fixed;z-index:9998;display:flex;align-items:center;border-radius:.5rem;cursor:pointer;line-break:anywhere;font-size:1.4rem;color:#000}.base64_tooltip svg[data-v-19fe372d]{margin-left:1rem;min-width:1.8rem}.base64_tooltip .button[data-v-19fe372d]{margin-top:1rem;margin-left:2rem}.msg[data-v-4dd6db95]{cursor:default;margin-bottom:2rem;background:white;display:flex;color:#000;font-size:1.4rem;box-sizing:border-box;border-radius:.4rem;box-shadow:0 0 1rem 1px silver}.msg.success .left[data-v-4dd6db95]{background:#40a9ff}.msg.warning .left[data-v-4dd6db95]{background:#c8c002}.msg.error .left[data-v-4dd6db95]{background:red}.msg .left[data-v-4dd6db95]{border-radius:.4rem 0 0 .4rem;display:flex;align-items:center;background:#40a9ff}.msg .left svg[data-v-4dd6db95]{margin:0 .3rem;cursor:pointer}.msg .right[data-v-4dd6db95]{flex:1;padding:1rem 2rem;display:flex;justify-content:space-between;align-items:center}.isNight .wrapper[data-v-66e306a2]{background:#22303f!important}.isNight .wrapper .title[data-v-66e306a2]{color:gray}.isNight .wrapper .option[data-v-66e306a2]{color:#fff!important}.isNight .wrapper .option span[data-v-66e306a2]{color:gray!important}.isNight .wrapper .white[data-v-66e306a2]{color:#fff!important}.tag-modal .wrapper[data-v-66e306a2]{z-index:9;background:#f1f1f1;border-radius:.8rem;font-size:1.4rem;padding:2rem 6rem 4rem;width:25rem}.tag-modal .wrapper .btns[data-v-66e306a2]{margin-top:1.5rem;display:flex;justify-content:flex-end;align-items:center;gap:1.5rem;font-size:1rem}.tag-modal .wrapper .btns div[data-v-66e306a2]{cursor:pointer}.tag-modal .wrapper .btns .main[data-v-66e306a2]{color:gray;background:#e2e2e2;padding:.5rem 1.2rem;border-radius:.4rem}.msgs[data-v-95974c3e]{position:fixed;margin-left:calc(50% - 25rem);width:50rem;z-index:9999;bottom:0;left:0;right:0}.isNight .open-post[data-v-3fb4d7c1],.isNight .nav[data-v-3fb4d7c1]{color:#fff;background:#18222d;border-bottom:1px solid #22303f}.card[data-v-3fb4d7c1]{border-radius:0 0 .4rem .4rem;overflow:hidden}.nav[data-v-3fb4d7c1]{font-size:1.4rem;background:white;padding:1rem;border-bottom:1px solid #e2e2e2} ');
  22.  
  23. (function (vue) {
  24. 'use strict';
  25.  
  26. var PageType = /* @__PURE__ */ ((PageType2) => {
  27. PageType2["Home"] = "Home";
  28. PageType2["Node"] = "Node";
  29. PageType2["Post"] = "Post";
  30. return PageType2;
  31. })(PageType || {});
  32. const _export_sfc = (sfc, props) => {
  33. const target = sfc.__vccOpts || sfc;
  34. for (const [key, val] of props) {
  35. target[key] = val;
  36. }
  37. return target;
  38. };
  39. const _sfc_main$g = {
  40. name: "Setting",
  41. inject: ["isNight"],
  42. props: {
  43. modelValue: {
  44. type: Object,
  45. default() {
  46. return {};
  47. }
  48. },
  49. show: {
  50. type: Boolean,
  51. default() {
  52. return false;
  53. }
  54. }
  55. },
  56. data() {
  57. return {
  58. config: window.clone(this.modelValue)
  59. };
  60. },
  61. watch: {
  62. config: {
  63. handler(n) {
  64. n.topReplyLoveMinCount = Math.trunc(n.topReplyLoveMinCount);
  65. if (n.topReplyLoveMinCount < 0) {
  66. n.topReplyLoveMinCount = 1;
  67. }
  68. this.$emit("update:modelValue", n);
  69. },
  70. deep: true
  71. }
  72. },
  73. created() {
  74. }
  75. };
  76. const _withScopeId$9 = (n) => (vue.pushScopeId("data-v-101664b8"), n = n(), vue.popScopeId(), n);
  77. const _hoisted_1$g = { class: "wrapper" };
  78. const _hoisted_2$e = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "title" }, " 脚本设置 ", -1));
  79. const _hoisted_3$b = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "sub-title" }, " 设置自动保存到本地,下次打开依然生效 ", -1));
  80. const _hoisted_4$8 = { class: "body" };
  81. const _hoisted_5$6 = { class: "option-list" };
  82. const _hoisted_6$6 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "option-title" }, "列表:", -1));
  83. const _hoisted_7$5 = { class: "option" };
  84. const _hoisted_8$5 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "列表帖子展示方式:", -1));
  85. const _hoisted_9$5 = { class: "option" };
  86. const _hoisted_10$5 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "列表hover时显示预览按钮:", -1));
  87. const _hoisted_11$5 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "notice" }, " 此项需要刷新页面才能生效 ", -1));
  88. const _hoisted_12$5 = { class: "option" };
  89. const _hoisted_13$5 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "点击列表的帖子,打开详情弹框 :", -1));
  90. const _hoisted_14$5 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "notice" }, " 若关闭此项,点击列表的帖子时,不会打开弹框,会跳转网页 ", -1));
  91. const _hoisted_15$5 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "option-title" }, "帖子:", -1));
  92. const _hoisted_16$3 = { class: "option" };
  93. const _hoisted_17$3 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "回复展示方式:", -1));
  94. const _hoisted_18$3 = { class: "option" };
  95. const _hoisted_19$3 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "单独打开帖子时默认显示楼中楼 :", -1));
  96. const _hoisted_20$3 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "notice" }, " 单独打开这种地址 https://v2ex.com/t/xxxx 时,是否默认显示楼中楼 ", -1));
  97. const _hoisted_21$3 = { class: "option" };
  98. const _hoisted_22$3 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "点击左右两侧透明处关闭帖子详情弹框:", -1));
  99. const _hoisted_23$2 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "option-title" }, "点赞:", -1));
  100. const _hoisted_24$3 = { class: "option" };
  101. const _hoisted_25$2 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "显示高赞回复:", -1));
  102. const _hoisted_26$2 = { class: "option" };
  103. const _hoisted_27$2 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "最多显示多少个高赞回复:", -1));
  104. const _hoisted_28$1 = { class: "option" };
  105. const _hoisted_29$1 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "最少需要多少赞才能被判定为高赞:", -1));
  106. const _hoisted_30$1 = { class: "option-list" };
  107. const _hoisted_31$1 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "option-title" }, "其他:", -1));
  108. const _hoisted_32$1 = { class: "option" };
  109. const _hoisted_33$1 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "显示工具栏:", -1));
  110. const _hoisted_34$1 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "notice" }, [
  111. /* @__PURE__ */ vue.createTextVNode(" 关闭此项会隐藏以下三个工具栏 "),
  112. /* @__PURE__ */ vue.createElementVNode("div", null, " 1. 首页”卡片/表格“ "),
  113. /* @__PURE__ */ vue.createElementVNode("div", null, " 2. 详情页”楼中楼/只看楼主/感谢/V2原版“ "),
  114. /* @__PURE__ */ vue.createElementVNode("div", null, " 3. 单独打开帖子时”点击显示楼中楼“ ")
  115. ], -1));
  116. const _hoisted_35$1 = { class: "option" };
  117. const _hoisted_36$1 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "新标签页打开链接 :", -1));
  118. const _hoisted_37 = { class: "option" };
  119. const _hoisted_38 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "用户打标签(跨平台,数据保存在自己的记事本):", -1));
  120. const _hoisted_39 = { class: "option" };
  121. const _hoisted_40 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "正文超长自动折叠:", -1));
  122. const _hoisted_41 = { class: "option" };
  123. const _hoisted_42 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "划词显示Base64解码框:", -1));
  124. const _hoisted_43 = { class: "option" };
  125. const _hoisted_44 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "使用 SOV2EX 搜索:", -1));
  126. const _hoisted_45 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "notice" }, " 此项需要刷新页面才能生效 ", -1));
  127. const _hoisted_46 = { class: "option" };
  128. const _hoisted_47 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "帖子宽度:", -1));
  129. const _hoisted_48 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "notice" }, [
  130. /* @__PURE__ */ vue.createTextVNode(" 默认为77rem。接受合法的width值: "),
  131. /* @__PURE__ */ vue.createElementVNode("a", {
  132. href: "https://vue3js.cn/interview/css/em_px_rem_vh_vw.html#%E4%BA%8C%E3%80%81%E5%8D%95%E4%BD%8D",
  133. target: "_blank"
  134. }, "rem、px、vw、vh"),
  135. /* @__PURE__ */ vue.createTextVNode("。 vw代表屏幕百分比,如想要屏幕的66%,请填写66vw ")
  136. ], -1));
  137. const _hoisted_49 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "notice" }, " 提示:此项设置以后,单独打开详情页时会出现帖子突然变宽(窄)的问题,暂时无解 ", -1));
  138. const _hoisted_50 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "option-title" }, "记忆阅读:", -1));
  139. const _hoisted_51 = { class: "option" };
  140. const _hoisted_52 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "记录上次阅读楼层(误差1层左右):", -1));
  141. const _hoisted_53 = { class: "option" };
  142. const _hoisted_54 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("span", null, "打开帖子自动跳转到上次阅读楼层:", -1));
  143. const _hoisted_55 = /* @__PURE__ */ _withScopeId$9(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "jieshao" }, null, -1));
  144. function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) {
  145. return vue.openBlock(), vue.createBlock(vue.Transition, null, {
  146. default: vue.withCtx(() => [
  147. $props.show ? (vue.openBlock(), vue.createElementBlock("div", {
  148. key: 0,
  149. class: vue.normalizeClass(["setting-modal modal", { isNight: $options.isNight }])
  150. }, [
  151. vue.createElementVNode("div", {
  152. class: "mask",
  153. onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("update:show", false))
  154. }),
  155. vue.createElementVNode("div", _hoisted_1$g, [
  156. _hoisted_2$e,
  157. _hoisted_3$b,
  158. vue.createElementVNode("div", _hoisted_4$8, [
  159. vue.createElementVNode("div", _hoisted_5$6, [
  160. _hoisted_6$6,
  161. vue.createElementVNode("div", _hoisted_7$5, [
  162. _hoisted_8$5,
  163. vue.createElementVNode("div", {
  164. class: vue.normalizeClass(["radio-group2", { isNight: $options.isNight }])
  165. }, [
  166. vue.createElementVNode("div", {
  167. class: vue.normalizeClass(["radio", $data.config.viewType === "table" ? "active" : ""]),
  168. onClick: _cache[1] || (_cache[1] = ($event) => $data.config.viewType = "table")
  169. }, "表格 ", 2),
  170. vue.createElementVNode("div", {
  171. class: vue.normalizeClass(["radio", $data.config.viewType === "card" ? "active" : ""]),
  172. onClick: _cache[2] || (_cache[2] = ($event) => $data.config.viewType = "card")
  173. }, "卡片 ", 2)
  174. ], 2)
  175. ]),
  176. vue.createElementVNode("div", _hoisted_9$5, [
  177. _hoisted_10$5,
  178. vue.createElementVNode("div", {
  179. class: vue.normalizeClass(["switch gray", { active: $data.config.showPreviewBtn, isNight: $options.isNight }]),
  180. onClick: _cache[3] || (_cache[3] = ($event) => $data.config.showPreviewBtn = !$data.config.showPreviewBtn)
  181. }, null, 2)
  182. ]),
  183. _hoisted_11$5,
  184. vue.createElementVNode("div", _hoisted_12$5, [
  185. _hoisted_13$5,
  186. vue.createElementVNode("div", {
  187. class: vue.normalizeClass(["switch gray", { active: $data.config.clickPostItemOpenDetail, isNight: $options.isNight }]),
  188. onClick: _cache[4] || (_cache[4] = ($event) => $data.config.clickPostItemOpenDetail = !$data.config.clickPostItemOpenDetail)
  189. }, null, 2)
  190. ]),
  191. _hoisted_14$5,
  192. _hoisted_15$5,
  193. vue.createElementVNode("div", _hoisted_16$3, [
  194. _hoisted_17$3,
  195. vue.createElementVNode("div", {
  196. class: vue.normalizeClass(["radio-group2", { isNight: $options.isNight }])
  197. }, [
  198. vue.createElementVNode("div", {
  199. class: vue.normalizeClass(["radio", $data.config.commentDisplayType === 0 ? "active" : ""]),
  200. onClick: _cache[5] || (_cache[5] = ($event) => $data.config.commentDisplayType = 0)
  201. }, "楼中楼 ", 2),
  202. vue.createElementVNode("div", {
  203. class: vue.normalizeClass(["radio", $data.config.commentDisplayType === 1 ? "active" : ""]),
  204. onClick: _cache[6] || (_cache[6] = ($event) => $data.config.commentDisplayType = 1)
  205. }, "感谢 ", 2),
  206. vue.createElementVNode("div", {
  207. class: vue.normalizeClass(["radio", $data.config.commentDisplayType === 3 ? "active" : ""]),
  208. onClick: _cache[7] || (_cache[7] = ($event) => $data.config.commentDisplayType = 3)
  209. }, "只看楼主 ", 2),
  210. vue.createElementVNode("div", {
  211. class: vue.normalizeClass(["radio", $data.config.commentDisplayType === 2 ? "active" : ""]),
  212. onClick: _cache[8] || (_cache[8] = ($event) => $data.config.commentDisplayType = 2)
  213. }, "V2原版 ", 2)
  214. ], 2)
  215. ]),
  216. vue.createElementVNode("div", _hoisted_18$3, [
  217. _hoisted_19$3,
  218. vue.createElementVNode("div", {
  219. class: vue.normalizeClass(["switch gray", { active: $data.config.autoOpenDetail, isNight: $options.isNight }]),
  220. onClick: _cache[9] || (_cache[9] = ($event) => $data.config.autoOpenDetail = !$data.config.autoOpenDetail)
  221. }, null, 2)
  222. ]),
  223. _hoisted_20$3,
  224. vue.createElementVNode("div", _hoisted_21$3, [
  225. _hoisted_22$3,
  226. vue.createElementVNode("div", {
  227. class: vue.normalizeClass(["switch gray", { active: $data.config.closePostDetailBySpace, isNight: $options.isNight }]),
  228. onClick: _cache[10] || (_cache[10] = ($event) => $data.config.closePostDetailBySpace = !$data.config.closePostDetailBySpace)
  229. }, null, 2)
  230. ]),
  231. _hoisted_23$2,
  232. vue.createElementVNode("div", _hoisted_24$3, [
  233. _hoisted_25$2,
  234. vue.createElementVNode("div", {
  235. class: vue.normalizeClass(["switch gray", { active: $data.config.showTopReply, isNight: $options.isNight }]),
  236. onClick: _cache[11] || (_cache[11] = ($event) => $data.config.showTopReply = !$data.config.showTopReply)
  237. }, null, 2)
  238. ]),
  239. vue.createElementVNode("div", _hoisted_26$2, [
  240. _hoisted_27$2,
  241. vue.withDirectives(vue.createElementVNode("input", {
  242. type: "number",
  243. min: "1",
  244. "onUpdate:modelValue": _cache[12] || (_cache[12] = ($event) => $data.config.topReplyCount = $event)
  245. }, null, 512), [
  246. [vue.vModelText, $data.config.topReplyCount]
  247. ])
  248. ]),
  249. vue.createElementVNode("div", _hoisted_28$1, [
  250. _hoisted_29$1,
  251. vue.withDirectives(vue.createElementVNode("input", {
  252. type: "number",
  253. min: "1",
  254. "onUpdate:modelValue": _cache[13] || (_cache[13] = ($event) => $data.config.topReplyLoveMinCount = $event)
  255. }, null, 512), [
  256. [vue.vModelText, $data.config.topReplyLoveMinCount]
  257. ])
  258. ])
  259. ]),
  260. vue.createElementVNode("div", _hoisted_30$1, [
  261. _hoisted_31$1,
  262. vue.createElementVNode("div", _hoisted_32$1, [
  263. _hoisted_33$1,
  264. vue.createElementVNode("div", {
  265. class: vue.normalizeClass(["switch gray", { active: $data.config.showToolbar, isNight: $options.isNight }]),
  266. onClick: _cache[14] || (_cache[14] = ($event) => $data.config.showToolbar = !$data.config.showToolbar)
  267. }, null, 2)
  268. ]),
  269. _hoisted_34$1,
  270. vue.createElementVNode("div", _hoisted_35$1, [
  271. _hoisted_36$1,
  272. vue.createElementVNode("div", {
  273. class: vue.normalizeClass(["switch gray", { active: $data.config.newTabOpen, isNight: $options.isNight }]),
  274. onClick: _cache[15] || (_cache[15] = ($event) => {
  275. $data.config.newTabOpen = !$data.config.newTabOpen;
  276. $data.config.clickPostItemOpenDetail = !$data.config.newTabOpen;
  277. })
  278. }, null, 2)
  279. ]),
  280. vue.createElementVNode("div", _hoisted_37, [
  281. _hoisted_38,
  282. vue.createElementVNode("div", {
  283. class: vue.normalizeClass(["switch gray", { active: $data.config.openTag, isNight: $options.isNight }]),
  284. onClick: _cache[16] || (_cache[16] = ($event) => $data.config.openTag = !$data.config.openTag)
  285. }, null, 2)
  286. ]),
  287. vue.createElementVNode("div", _hoisted_39, [
  288. _hoisted_40,
  289. vue.createElementVNode("div", {
  290. class: vue.normalizeClass(["switch gray", { active: $data.config.contentAutoCollapse, isNight: $options.isNight }]),
  291. onClick: _cache[17] || (_cache[17] = ($event) => $data.config.contentAutoCollapse = !$data.config.contentAutoCollapse)
  292. }, null, 2)
  293. ]),
  294. vue.createElementVNode("div", _hoisted_41, [
  295. _hoisted_42,
  296. vue.createElementVNode("div", {
  297. class: vue.normalizeClass(["switch gray", { active: $data.config.base64, isNight: $options.isNight }]),
  298. onClick: _cache[18] || (_cache[18] = ($event) => $data.config.base64 = !$data.config.base64)
  299. }, null, 2)
  300. ]),
  301. vue.createElementVNode("div", _hoisted_43, [
  302. _hoisted_44,
  303. vue.createElementVNode("div", {
  304. class: vue.normalizeClass(["switch gray", { active: $data.config.sov2ex, isNight: $options.isNight }]),
  305. onClick: _cache[19] || (_cache[19] = ($event) => $data.config.sov2ex = !$data.config.sov2ex)
  306. }, null, 2)
  307. ]),
  308. _hoisted_45,
  309. vue.createElementVNode("div", _hoisted_46, [
  310. _hoisted_47,
  311. vue.withDirectives(vue.createElementVNode("input", {
  312. type: "text",
  313. "onUpdate:modelValue": _cache[20] || (_cache[20] = ($event) => $data.config.postWidth = $event)
  314. }, null, 512), [
  315. [vue.vModelText, $data.config.postWidth]
  316. ])
  317. ]),
  318. _hoisted_48,
  319. _hoisted_49,
  320. _hoisted_50,
  321. vue.createElementVNode("div", _hoisted_51, [
  322. _hoisted_52,
  323. vue.createElementVNode("div", {
  324. class: vue.normalizeClass(["switch gray", { active: $data.config.rememberLastReadFloor, isNight: $options.isNight }]),
  325. onClick: _cache[21] || (_cache[21] = ($event) => {
  326. $data.config.rememberLastReadFloor = !$data.config.rememberLastReadFloor;
  327. $data.config.autoJumpLastReadFloor = false;
  328. })
  329. }, null, 2)
  330. ]),
  331. vue.createElementVNode("div", _hoisted_53, [
  332. _hoisted_54,
  333. vue.createElementVNode("div", {
  334. class: vue.normalizeClass(["switch gray", { active: $data.config.autoJumpLastReadFloor, isNight: $options.isNight }]),
  335. onClick: _cache[22] || (_cache[22] = ($event) => $data.config.autoJumpLastReadFloor = !$data.config.autoJumpLastReadFloor)
  336. }, null, 2)
  337. ])
  338. ])
  339. ]),
  340. _hoisted_55
  341. ])
  342. ], 2)) : vue.createCommentVNode("", true)
  343. ]),
  344. _: 1
  345. });
  346. }
  347. const Setting = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["render", _sfc_render$a], ["__scopeId", "data-v-101664b8"]]);
  348. const eventBus = {
  349. eventMap: /* @__PURE__ */ new Map(),
  350. on(eventType, cb) {
  351. let cbs = this.eventMap.get(eventType);
  352. if (cbs) {
  353. cbs.push(cb);
  354. } else {
  355. cbs = [cb];
  356. }
  357. this.eventMap.set(eventType, cbs);
  358. },
  359. emit(eventType, val) {
  360. let cbs = this.eventMap.get(eventType);
  361. if (cbs) {
  362. cbs.map((cb) => cb(val));
  363. }
  364. },
  365. off(eventType) {
  366. let cbs = this.eventMap.has(eventType);
  367. if (cbs) {
  368. this.eventMap.delete(eventType);
  369. }
  370. },
  371. clear() {
  372. this.eventMap = /* @__PURE__ */ new Map();
  373. }
  374. };
  375. const CMD$1 = {
  376. SHOW_TOOLTIP: "SHOW_TOOLTIP",
  377. SHOW_MSG: "SHOW_MSG",
  378. SET_CALL: "SET_CALL",
  379. SHOW_CALL: "SHOW_CALL",
  380. REFRESH_ONCE: "REFRESH_ONCE",
  381. ADD_REPLY: "ADD_REPLY",
  382. IGNORE: "IGNORE",
  383. MERGE: "MERGE",
  384. REMOVE: "REMOVE",
  385. CHANGE_COMMENT_THANK: "CHANGE_COMMENT_THANK",
  386. CHANGE_POST_THANK: "CHANGE_POST_THANK",
  387. ADD_TAG: "ADD_TAG",
  388. REMOVE_TAG: "REMOVE_TAG",
  389. RELATION_REPLY: "RELATION_REPLY",
  390. JUMP: "JUMP",
  391. ADD_READ: "ADD_READ"
  392. };
  393. const _sfc_main$f = {
  394. name: "PopConfirm",
  395. props: {
  396. title: {
  397. type: String,
  398. default() {
  399. return "";
  400. }
  401. },
  402. disabled: {
  403. type: Boolean,
  404. default() {
  405. return false;
  406. }
  407. }
  408. },
  409. data() {
  410. return {
  411. show: false
  412. };
  413. },
  414. methods: {
  415. showPop(e) {
  416. if (this.disabled)
  417. return;
  418. let rect = e.target.getBoundingClientRect();
  419. this.show = true;
  420. vue.nextTick(() => {
  421. this.$refs.tip.style.top = rect.top + "px";
  422. this.$refs.tip.style.left = rect.left + rect.width / 2 - 50 + "px";
  423. });
  424. },
  425. confirm() {
  426. this.show = false;
  427. this.$emit("confirm");
  428. }
  429. }
  430. };
  431. const _hoisted_1$f = { class: "pop-confirm" };
  432. const _hoisted_2$d = {
  433. key: 0,
  434. ref: "tip",
  435. class: "pop-confirm-content"
  436. };
  437. const _hoisted_3$a = { class: "text" };
  438. const _hoisted_4$7 = { class: "options" };
  439. function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
  440. return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$f, [
  441. (vue.openBlock(), vue.createBlock(vue.Teleport, { to: "body" }, [
  442. vue.createVNode(vue.Transition, null, {
  443. default: vue.withCtx(() => [
  444. $data.show ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$d, [
  445. vue.createElementVNode("div", _hoisted_3$a, vue.toDisplayString($props.title), 1),
  446. vue.createElementVNode("div", _hoisted_4$7, [
  447. vue.createElementVNode("div", {
  448. onClick: _cache[0] || (_cache[0] = ($event) => $data.show = false)
  449. }, "取消"),
  450. vue.createElementVNode("div", {
  451. class: "main",
  452. onClick: _cache[1] || (_cache[1] = (...args) => $options.confirm && $options.confirm(...args))
  453. }, "确认")
  454. ])
  455. ], 512)) : vue.createCommentVNode("", true)
  456. ]),
  457. _: 1
  458. })
  459. ])),
  460. vue.createElementVNode("span", {
  461. onClick: _cache[2] || (_cache[2] = (...args) => $options.showPop && $options.showPop(...args))
  462. }, [
  463. vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
  464. ])
  465. ]);
  466. }
  467. const PopConfirm = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["render", _sfc_render$9], ["__scopeId", "data-v-8df5d12b"]]);
  468. const loveColor = "rgb(224,42,42)";
  469. const _sfc_main$e = {
  470. name: "Point",
  471. components: { PopConfirm },
  472. inject: ["post", "isLogin"],
  473. props: {
  474. item: {
  475. type: Object,
  476. default() {
  477. return {};
  478. }
  479. },
  480. full: {
  481. type: Boolean,
  482. default() {
  483. return true;
  484. }
  485. },
  486. apiUrl: ""
  487. },
  488. computed: {
  489. disabled() {
  490. return this.item.username === window.user.username || this.item.isThanked || !this.isLogin;
  491. }
  492. },
  493. methods: {
  494. getColor() {
  495. if (this.item.isThanked)
  496. return loveColor;
  497. return this.full ? loveColor : "#929596";
  498. },
  499. getIsFull() {
  500. if (this.item.isThanked)
  501. return loveColor;
  502. return this.full ? loveColor : "none";
  503. },
  504. thankError() {
  505. if (this.item.username === window.user.username) {
  506. return eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "不能感谢自己" });
  507. }
  508. if (this.item.isThanked) {
  509. return eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "已经感谢过了" });
  510. }
  511. if (!this.isLogin) {
  512. return eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "请先登录!" });
  513. }
  514. },
  515. async thank() {
  516. this.$emit("addThank");
  517. let url = `${window.baseUrl}/thank/${this.apiUrl}?once=${this.post.once}`;
  518. $.post(url).then((res) => {
  519. if (!res.success) {
  520. this.$emit("recallThank");
  521. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: res.message });
  522. }
  523. eventBus.emit(CMD$1.REFRESH_ONCE, res.once);
  524. }, (err) => {
  525. this.$emit("recallThank");
  526. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "感谢失败" });
  527. eventBus.emit(CMD$1.REFRESH_ONCE);
  528. });
  529. }
  530. }
  531. };
  532. const _hoisted_1$e = { class: "point" };
  533. const _hoisted_2$c = ["fill", "stroke"];
  534. const _hoisted_3$9 = { class: "num" };
  535. function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
  536. const _component_PopConfirm = vue.resolveComponent("PopConfirm");
  537. return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$e, [
  538. vue.createVNode(_component_PopConfirm, {
  539. disabled: $options.disabled,
  540. title: `确认花费 10 个铜币向 @${$props.item.username} 的这条回复发送感谢?`,
  541. onConfirm: $options.thank
  542. }, {
  543. default: vue.withCtx(() => [
  544. vue.createElementVNode("div", {
  545. class: "up",
  546. onClick: _cache[0] || (_cache[0] = (...args) => $options.thankError && $options.thankError(...args))
  547. }, [
  548. (vue.openBlock(), vue.createElementBlock("svg", {
  549. class: vue.normalizeClass({ disabled: $options.disabled }),
  550. width: "19",
  551. height: "19",
  552. viewBox: "0 0 48 48",
  553. fill: "none",
  554. xmlns: "http://www.w3.org/2000/svg"
  555. }, [
  556. vue.createElementVNode("path", {
  557. d: "M15 8C8.92487 8 4 12.9249 4 19C4 30 17 40 24 42.3262C31 40 44 30 44 19C44 12.9249 39.0751 8 33 8C29.2797 8 25.9907 9.8469 24 12.6738C22.0093 9.8469 18.7203 8 15 8Z",
  558. fill: $options.getIsFull(),
  559. stroke: $options.getColor(),
  560. "stroke-width": "2",
  561. "stroke-linecap": "round",
  562. "stroke-linejoin": "round"
  563. }, null, 8, _hoisted_2$c)
  564. ], 2))
  565. ])
  566. ]),
  567. _: 1
  568. }, 8, ["disabled", "title", "onConfirm"]),
  569. vue.createElementVNode("div", _hoisted_3$9, vue.toDisplayString($props.item.thankCount ? $props.item.thankCount : "感谢"), 1)
  570. ]);
  571. }
  572. const Point = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["render", _sfc_render$8], ["__scopeId", "data-v-810a119b"]]);
  573. const _sfc_main$d = {
  574. name: "Author",
  575. components: { PopConfirm, Point },
  576. inject: ["isLogin", "tags", "config"],
  577. props: {
  578. modelValue: false,
  579. comment: {
  580. type: Object,
  581. default() {
  582. return {};
  583. }
  584. },
  585. type: {
  586. type: String,
  587. default() {
  588. return "list";
  589. }
  590. }
  591. },
  592. computed: {
  593. isDev() {
  594. return false;
  595. },
  596. pointInfo() {
  597. return {
  598. isThanked: this.comment.isThanked,
  599. thankCount: this.comment.thankCount,
  600. username: this.comment.username
  601. };
  602. },
  603. myTags() {
  604. return this.tags[this.comment.username] ?? [];
  605. },
  606. context() {
  607. return this.comment.replyUsers.length;
  608. }
  609. },
  610. methods: {
  611. jump() {
  612. eventBus.emit(CMD$1.JUMP, this.comment.floor);
  613. },
  614. showRelationReply() {
  615. if (!this.comment.replyUsers.length) {
  616. eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "该回复无上下文" });
  617. return;
  618. }
  619. eventBus.emit(CMD$1.RELATION_REPLY, {
  620. left: this.comment.replyUsers,
  621. right: this.comment.username,
  622. rightFloor: this.comment.floor
  623. });
  624. },
  625. addTag() {
  626. eventBus.emit(CMD$1.ADD_TAG, this.comment.username);
  627. },
  628. removeTag(tag) {
  629. eventBus.emit(CMD$1.REMOVE_TAG, { username: this.comment.username, tag });
  630. },
  631. checkIsLogin(emitName = "") {
  632. if (!this.isLogin) {
  633. eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "请先登录!" });
  634. return false;
  635. }
  636. this.$emit(emitName);
  637. return true;
  638. },
  639. addThank() {
  640. eventBus.emit(CMD$1.CHANGE_COMMENT_THANK, { id: this.comment.id, type: "add" });
  641. },
  642. recallThank() {
  643. eventBus.emit(CMD$1.CHANGE_COMMENT_THANK, { id: this.comment.id, type: "recall" });
  644. }
  645. }
  646. };
  647. const _withScopeId$8 = (n) => (vue.pushScopeId("data-v-d8c00e09"), n = n(), vue.popScopeId(), n);
  648. const _hoisted_1$d = { class: "Author-left" };
  649. const _hoisted_2$b = /* @__PURE__ */ _withScopeId$8(() => /* @__PURE__ */ vue.createElementVNode("path", {
  650. d: "M22 42H6V26",
  651. stroke: "#177EC9",
  652. "stroke-width": "4",
  653. "stroke-linecap": "round",
  654. "stroke-linejoin": "round"
  655. }, null, -1));
  656. const _hoisted_3$8 = /* @__PURE__ */ _withScopeId$8(() => /* @__PURE__ */ vue.createElementVNode("path", {
  657. d: "M26 6H42V22",
  658. stroke: "#177EC9",
  659. "stroke-width": "4",
  660. "stroke-linecap": "round",
  661. "stroke-linejoin": "round"
  662. }, null, -1));
  663. const _hoisted_4$6 = [
  664. _hoisted_2$b,
  665. _hoisted_3$8
  666. ];
  667. const _hoisted_5$5 = ["href"];
  668. const _hoisted_6$5 = ["src"];
  669. const _hoisted_7$4 = { class: "texts" };
  670. const _hoisted_8$4 = ["href"];
  671. const _hoisted_9$4 = {
  672. key: 0,
  673. class: "op"
  674. };
  675. const _hoisted_10$4 = {
  676. key: 1,
  677. class: "mod"
  678. };
  679. const _hoisted_11$4 = { class: "ago" };
  680. const _hoisted_12$4 = { class: "my-tag" };
  681. const _hoisted_13$4 = /* @__PURE__ */ _withScopeId$8(() => /* @__PURE__ */ vue.createElementVNode("i", { class: "fa fa-tag" }, null, -1));
  682. const _hoisted_14$4 = ["onClick"];
  683. const _hoisted_15$4 = { class: "Author-right" };
  684. const _hoisted_16$2 = {
  685. key: 0,
  686. class: "toolbar"
  687. };
  688. const _hoisted_17$2 = /* @__PURE__ */ _withScopeId$8(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "tool" }, [
  689. /* @__PURE__ */ vue.createElementVNode("span", null, "隐藏")
  690. ], -1));
  691. const _hoisted_18$2 = /* @__PURE__ */ _withScopeId$8(() => /* @__PURE__ */ vue.createElementVNode("span", null, "上下文", -1));
  692. const _hoisted_19$2 = [
  693. _hoisted_18$2
  694. ];
  695. const _hoisted_20$2 = /* @__PURE__ */ _withScopeId$8(() => /* @__PURE__ */ vue.createElementVNode("span", null, "跳转", -1));
  696. const _hoisted_21$2 = [
  697. _hoisted_20$2
  698. ];
  699. const _hoisted_22$2 = /* @__PURE__ */ vue.createStaticVNode('<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" data-v-d8c00e09><path d="M4 6H44V36H29L24 41L19 36H4V6Z" fill="none" stroke="#929596" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-v-d8c00e09></path><path d="M23 21H25.0025" stroke="#929596" stroke-width="2" stroke-linecap="round" data-v-d8c00e09></path><path d="M33.001 21H34.9999" stroke="#929596" stroke-width="2" stroke-linecap="round" data-v-d8c00e09></path><path d="M13.001 21H14.9999" stroke="#929596" stroke-width="2" stroke-linecap="round" data-v-d8c00e09></path></svg><span data-v-d8c00e09>回复</span>', 2);
  700. const _hoisted_24$2 = [
  701. _hoisted_22$2
  702. ];
  703. function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
  704. const _component_PopConfirm = vue.resolveComponent("PopConfirm");
  705. const _component_Point = vue.resolveComponent("Point");
  706. return vue.openBlock(), vue.createElementBlock("div", {
  707. class: vue.normalizeClass(["Author", { expand: !$props.modelValue }])
  708. }, [
  709. vue.createElementVNode("div", _hoisted_1$d, [
  710. !$props.modelValue ? (vue.openBlock(), vue.createElementBlock("svg", {
  711. key: 0,
  712. class: "expand-icon",
  713. onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("update:modelValue", true)),
  714. width: "24",
  715. height: "24",
  716. viewBox: "0 0 48 48",
  717. fill: "none",
  718. xmlns: "http://www.w3.org/2000/svg"
  719. }, _hoisted_4$6)) : vue.createCommentVNode("", true),
  720. vue.createElementVNode("a", {
  721. class: "avatar",
  722. href: `/member/${$props.comment.username}`
  723. }, [
  724. vue.createElementVNode("img", {
  725. src: $props.comment.avatar,
  726. alt: ""
  727. }, null, 8, _hoisted_6$5)
  728. ], 8, _hoisted_5$5),
  729. vue.createElementVNode("span", _hoisted_7$4, [
  730. vue.createElementVNode("strong", null, [
  731. vue.createElementVNode("a", {
  732. href: `/member/${$props.comment.username}`,
  733. class: "username"
  734. }, vue.toDisplayString($props.comment.username), 9, _hoisted_8$4)
  735. ]),
  736. $props.comment.isOp ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9$4, "OP")) : vue.createCommentVNode("", true),
  737. $props.comment.isMod ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$4, "MOD")) : vue.createCommentVNode("", true),
  738. vue.createElementVNode("span", _hoisted_11$4, vue.toDisplayString($props.comment.date), 1),
  739. $options.isLogin && $options.config.openTag ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 2 }, [
  740. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($options.myTags, (i) => {
  741. return vue.openBlock(), vue.createElementBlock("span", _hoisted_12$4, [
  742. _hoisted_13$4,
  743. vue.createElementVNode("span", null, vue.toDisplayString(i), 1),
  744. vue.createElementVNode("i", {
  745. class: "fa fa-trash-o remove",
  746. onClick: ($event) => $options.removeTag(i)
  747. }, null, 8, _hoisted_14$4)
  748. ]);
  749. }), 256)),
  750. vue.createElementVNode("span", {
  751. class: "add-tag ago",
  752. onClick: _cache[1] || (_cache[1] = (...args) => $options.addTag && $options.addTag(...args)),
  753. title: "添加标签"
  754. }, "+")
  755. ], 64)) : vue.createCommentVNode("", true)
  756. ])
  757. ]),
  758. vue.createElementVNode("div", _hoisted_15$4, [
  759. $options.isLogin ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_16$2, [
  760. vue.createVNode(_component_PopConfirm, {
  761. title: "确认隐藏这条回复?",
  762. onConfirm: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("hide"))
  763. }, {
  764. default: vue.withCtx(() => [
  765. _hoisted_17$2
  766. ]),
  767. _: 1
  768. }),
  769. $options.context ? (vue.openBlock(), vue.createElementBlock("div", {
  770. key: 0,
  771. class: "tool",
  772. onClick: _cache[3] || (_cache[3] = (...args) => $options.showRelationReply && $options.showRelationReply(...args))
  773. }, _hoisted_19$2)) : vue.createCommentVNode("", true),
  774. $props.type === "top" ? (vue.openBlock(), vue.createElementBlock("div", {
  775. key: 1,
  776. class: "tool",
  777. onClick: _cache[4] || (_cache[4] = (...args) => $options.jump && $options.jump(...args))
  778. }, _hoisted_21$2)) : vue.createCommentVNode("", true),
  779. vue.createElementVNode("div", {
  780. class: "tool",
  781. onClick: _cache[5] || (_cache[5] = ($event) => $options.checkIsLogin("reply"))
  782. }, _hoisted_24$2),
  783. vue.withDirectives(vue.createVNode(_component_Point, {
  784. item: $options.pointInfo,
  785. onAddThank: $options.addThank,
  786. onRecallThank: $options.recallThank,
  787. "api-url": "reply/" + $props.comment.id
  788. }, null, 8, ["item", "onAddThank", "onRecallThank", "api-url"]), [
  789. [vue.vShow, !$props.comment.thankCount]
  790. ])
  791. ])) : vue.createCommentVNode("", true),
  792. vue.withDirectives(vue.createVNode(_component_Point, {
  793. item: $options.pointInfo,
  794. onAddThank: $options.addThank,
  795. onRecallThank: $options.recallThank,
  796. "api-url": "reply/" + $props.comment.id
  797. }, null, 8, ["item", "onAddThank", "onRecallThank", "api-url"]), [
  798. [vue.vShow, $props.comment.thankCount]
  799. ]),
  800. vue.createElementVNode("div", {
  801. class: vue.normalizeClass(["floor", { isDev: $options.isDev }])
  802. }, vue.toDisplayString($options.isDev ? `a${$props.comment.floor}-` : $props.comment.floor), 3)
  803. ])
  804. ], 2);
  805. }
  806. const Author = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["render", _sfc_render$7], ["__scopeId", "data-v-d8c00e09"]]);
  807. const _withScopeId$7 = (n) => (vue.pushScopeId("data-v-0cd7749c"), n = n(), vue.popScopeId(), n);
  808. const _hoisted_1$c = { class: "get-cursor" };
  809. const _hoisted_2$a = ["innerHTML"];
  810. const _hoisted_3$7 = { class: "toolbar" };
  811. const _hoisted_4$5 = /* @__PURE__ */ _withScopeId$7(() => /* @__PURE__ */ vue.createElementVNode("span", null, "请尽量让自己的回复能够对别人有帮助", -1));
  812. const _sfc_main$c = {
  813. __name: "PostEditor",
  814. props: {
  815. replyUser: null,
  816. replyFloor: null,
  817. useType: {
  818. type: String,
  819. default() {
  820. return "reply-comment";
  821. }
  822. }
  823. },
  824. emits: ["close"],
  825. setup(__props, { expose, emit: emits }) {
  826. const props = __props;
  827. const { replyUser, replyFloor, useType } = props;
  828. const replyInfo = replyUser ? `@${replyUser} #${replyFloor} ` : "";
  829. const post = vue.inject("post");
  830. vue.inject("show");
  831. vue.inject("pageType");
  832. const allReplyUsers = vue.inject("allReplyUsers");
  833. let isFocus = vue.ref(false);
  834. const loading = vue.ref(false);
  835. const editorId = vue.ref("editorId_" + Date.now());
  836. const content = vue.ref(replyInfo);
  837. const txtRef = vue.ref(null);
  838. const cursorRef = vue.ref(null);
  839. const none = vue.ref('<span style="white-space:pre-wrap;"> </span>');
  840. expose({ content });
  841. const editorClass = vue.computed(() => {
  842. return [useType, isFocus.value ? "isFocus" : ""];
  843. });
  844. const cursorHtml = vue.computed(() => {
  845. var _a;
  846. if (!txtRef.value || !content.value)
  847. return "";
  848. let index2 = ((_a = txtRef.value) == null ? void 0 : _a.selectionStart) || 0;
  849. return content.value.substring(0, index2).replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<br/>").replace(/\s/g, none.value);
  850. });
  851. const disabled = vue.computed(() => {
  852. if (content.value) {
  853. return content.value === replyInfo;
  854. } else {
  855. return true;
  856. }
  857. });
  858. async function submit() {
  859. if (disabled.value || loading.value)
  860. return;
  861. loading.value = true;
  862. let item = {
  863. thankCount: 0,
  864. isThanked: false,
  865. isOp: post.value.username === window.user.username,
  866. id: Date.now(),
  867. username: window.user.username,
  868. avatar: window.user.avatar,
  869. date: "几秒前",
  870. floor: post.value.replyCount + 1,
  871. reply_content: content.value || Date.now(),
  872. children: [],
  873. replyUsers: replyUser ? [replyUser] : [],
  874. replyFloor: replyFloor || -1
  875. };
  876. let matchUsers = content.value.match(/@([\w]+?[\s])/g);
  877. if (matchUsers) {
  878. matchUsers.map((i) => {
  879. let username = i.replace("@", "").replace(" ", "");
  880. item.reply_content = item.reply_content.replace(username, `<a href="/member/${username}">${username}</a>`);
  881. });
  882. }
  883. let url = `${window.baseUrl}/t/${post.value.id}`;
  884. $.post(url, { content: content.value, once: post.value.once }).then(
  885. (res) => {
  886. console.log("回复", res);
  887. loading.value = false;
  888. let r = res.search("你上一条回复的内容和这条相同");
  889. if (r > -1)
  890. return eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "你上一条回复的内容和这条相同" });
  891. r = res.search("请不要在每一个回复中都包括外链,这看起来像是在 spamming");
  892. if (r > -1)
  893. return eventBus.emit(CMD$1.SHOW_MSG, {
  894. type: "error",
  895. text: "请不要在每一个回复中都包括外链,这看起来像是在 spamming"
  896. });
  897. let r2 = res.search("创建新回复");
  898. if (r2 > -1) {
  899. eventBus.emit(CMD$1.REFRESH_ONCE, res);
  900. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "回复失败" });
  901. let clientWidth = window.win().document.body.clientWidth;
  902. let windowWidth = 1200;
  903. let left = clientWidth / 2 - windowWidth / 2;
  904. let newWin = window.win().open("about:blank", "hello", `width=${windowWidth},height=600,left=${left},top=100`);
  905. newWin.document.write(res);
  906. return;
  907. }
  908. content.value = replyInfo;
  909. emits("close");
  910. eventBus.emit(CMD$1.REFRESH_ONCE, res);
  911. eventBus.emit(CMD$1.SHOW_MSG, { type: "success", text: "回复成功" });
  912. eventBus.emit(CMD$1.ADD_REPLY, item);
  913. },
  914. (err) => {
  915. console.log("err", err);
  916. loading.value = false;
  917. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "回复失败" });
  918. }
  919. ).catch((r) => {
  920. console.log("cathc", r);
  921. });
  922. }
  923. function off() {
  924. eventBus.emit(CMD$1.SHOW_CALL, { show: false });
  925. eventBus.off(CMD$1.SET_CALL);
  926. }
  927. function checkHeight() {
  928. txtRef.value.style.height = 0;
  929. txtRef.value.style.height = txtRef.value.scrollHeight + "px";
  930. }
  931. function showCallPopover(text) {
  932. let r = cursorRef.value.getBoundingClientRect();
  933. eventBus.emit(CMD$1.SHOW_CALL, { show: true, top: r.top, left: r.left, text });
  934. eventBus.off(CMD$1.SET_CALL);
  935. eventBus.on(CMD$1.SET_CALL, (e) => {
  936. let cursorPos = txtRef.value.selectionStart;
  937. let start = content.value.slice(0, cursorPos);
  938. let end = content.value.slice(cursorPos, content.value.length);
  939. let lastCallPos = start.lastIndexOf("@");
  940. start = content.value.slice(0, lastCallPos + 1);
  941. if (e === "管理员") {
  942. e = "Livid @Kai @Olivia @GordianZ @sparanoid";
  943. setTimeout(checkHeight);
  944. }
  945. if (e === "所有人") {
  946. e = allReplyUsers.value.map((v, i) => {
  947. if (i)
  948. return "@" + v;
  949. else
  950. return v;
  951. }).join(" ");
  952. setTimeout(checkHeight);
  953. }
  954. content.value = start + e + " " + end;
  955. let moveCursorPos = start.length + e.length + 1;
  956. setTimeout(() => {
  957. txtRef.value.setSelectionRange(moveCursorPos, moveCursorPos);
  958. });
  959. eventBus.off(CMD$1.SET_CALL);
  960. });
  961. }
  962. function onKeydown(e) {
  963. let code = e.keyCode;
  964. switch (code) {
  965. case 8:
  966. if (content.value === "@") {
  967. off();
  968. }
  969. break;
  970. case 37:
  971. case 38:
  972. case 39:
  973. case 40:
  974. setTimeout(() => onInput({ data: "" }), 100);
  975. break;
  976. case 27:
  977. e.preventDefault();
  978. e.stopPropagation();
  979. e.stopImmediatePropagation();
  980. return false;
  981. }
  982. }
  983. function onInput(e) {
  984. let cursorPos = txtRef.value.selectionStart;
  985. if (!content.value)
  986. return;
  987. if (e.data === " ") {
  988. return off();
  989. }
  990. if (e.data === "@") {
  991. if (content.value.length !== 1) {
  992. if (content.value[cursorPos - 2] === " " || content.value[cursorPos - 2] === "\n") {
  993. return showCallPopover("");
  994. }
  995. } else {
  996. return showCallPopover("");
  997. }
  998. off();
  999. } else {
  1000. let judgeStr = content.value.slice(0, cursorPos);
  1001. let lastCallPos = judgeStr.lastIndexOf("@");
  1002. if (lastCallPos === -1) {
  1003. return off();
  1004. }
  1005. let callStr = judgeStr.slice(lastCallPos, cursorPos);
  1006. let hasSpace = callStr.includes(" ");
  1007. if (hasSpace) {
  1008. off();
  1009. } else {
  1010. if (lastCallPos === 0) {
  1011. return showCallPopover(callStr.replace("@", ""));
  1012. }
  1013. if (content.value.length !== 1) {
  1014. if (content.value[lastCallPos - 1] === " " || content.value[lastCallPos - 1] === "\n") {
  1015. return showCallPopover(callStr.replace("@", ""));
  1016. }
  1017. } else {
  1018. return showCallPopover(callStr.replace("@", ""));
  1019. }
  1020. off();
  1021. }
  1022. }
  1023. }
  1024. function onBlur() {
  1025. isFocus.value = false;
  1026. }
  1027. vue.onMounted(() => {
  1028. $(`.${editorId.value}`).each(function() {
  1029. this.setAttribute("style", "height:" + this.scrollHeight + "px;overflow-y:hidden;");
  1030. }).on("input", function() {
  1031. this.style.height = 0;
  1032. this.style.height = this.scrollHeight + "px";
  1033. });
  1034. if (useType === "reply-comment") {
  1035. txtRef.value && txtRef.value.focus();
  1036. }
  1037. });
  1038. vue.onBeforeUnmount(() => {
  1039. $(`.${editorId.value}`).off();
  1040. });
  1041. return (_ctx, _cache) => {
  1042. return vue.openBlock(), vue.createElementBlock("div", {
  1043. class: vue.normalizeClass(["post-editor-wrapper", vue.unref(editorClass)])
  1044. }, [
  1045. vue.withDirectives(vue.createElementVNode("textarea", {
  1046. class: vue.normalizeClass(["post-editor", editorId.value]),
  1047. ref_key: "txtRef",
  1048. ref: txtRef,
  1049. onFocus: _cache[0] || (_cache[0] = ($event) => vue.isRef(isFocus) ? isFocus.value = true : isFocus = true),
  1050. onBlur,
  1051. onInput,
  1052. onKeydown,
  1053. "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => content.value = $event)
  1054. }, null, 34), [
  1055. [vue.vModelText, content.value]
  1056. ]),
  1057. vue.createElementVNode("div", _hoisted_1$c, [
  1058. vue.createElementVNode("span", { innerHTML: vue.unref(cursorHtml) }, null, 8, _hoisted_2$a),
  1059. vue.createElementVNode("span", {
  1060. class: "cursor",
  1061. ref_key: "cursorRef",
  1062. ref: cursorRef
  1063. }, "|", 512)
  1064. ]),
  1065. vue.createElementVNode("div", _hoisted_3$7, [
  1066. _hoisted_4$5,
  1067. vue.createElementVNode("div", {
  1068. class: vue.normalizeClass(["button", { disabled: vue.unref(disabled), loading: loading.value }]),
  1069. onClick: submit
  1070. }, "回复 ", 2)
  1071. ])
  1072. ], 2);
  1073. };
  1074. }
  1075. };
  1076. const PostEditor = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__scopeId", "data-v-0cd7749c"]]);
  1077. const _hoisted_1$b = {
  1078. key: 0,
  1079. class: "html-wrapper"
  1080. };
  1081. const _hoisted_2$9 = ["innerHTML"];
  1082. const _sfc_main$b = {
  1083. __name: "BaseHtmlRender",
  1084. props: ["html"],
  1085. setup(__props) {
  1086. const props = __props;
  1087. const config = vue.inject("config");
  1088. const contentRef = vue.ref(null);
  1089. const checkHeight = 900;
  1090. const mask = vue.ref(false);
  1091. const handOpen = vue.ref(false);
  1092. function mouseup(e) {
  1093. if (!config.value.base64)
  1094. return;
  1095. let selectionText = window.win().getSelection().toString();
  1096. if (selectionText) {
  1097. let r = selectionText.match(/([A-Za-z0-9+/=]+)/g);
  1098. if (r) {
  1099. if (r[0].length < 4)
  1100. return;
  1101. eventBus.emit(CMD$1.SHOW_TOOLTIP, { text: r[0], e });
  1102. }
  1103. }
  1104. }
  1105. vue.watch(config.value, (newVale) => {
  1106. if (!newVale.contentAutoCollapse) {
  1107. mask.value = false;
  1108. }
  1109. });
  1110. vue.watch([() => contentRef.value, () => props.html], () => {
  1111. if (!contentRef.value || !props.html)
  1112. return;
  1113. if (!config.value.contentAutoCollapse)
  1114. return;
  1115. contentRef.value.querySelectorAll("img").forEach((item) => {
  1116. item.removeEventListener("load", checkContentHeight);
  1117. item.addEventListener("load", checkContentHeight);
  1118. });
  1119. checkContentHeight();
  1120. }, { immediate: true, flush: "post" });
  1121. function checkContentHeight() {
  1122. if (handOpen.value)
  1123. return;
  1124. let rect = contentRef.value.getBoundingClientRect();
  1125. mask.value = rect.height >= checkHeight;
  1126. }
  1127. return (_ctx, _cache) => {
  1128. return props.html ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$b, [
  1129. vue.createElementVNode("div", {
  1130. class: vue.normalizeClass({ mask: mask.value })
  1131. }, [
  1132. vue.createElementVNode("div", vue.mergeProps({
  1133. ref_key: "contentRef",
  1134. ref: contentRef
  1135. }, _ctx.$attrs, {
  1136. innerHTML: props.html,
  1137. onMouseup: mouseup
  1138. }), null, 16, _hoisted_2$9)
  1139. ], 2),
  1140. mask.value ? (vue.openBlock(), vue.createElementBlock("div", {
  1141. key: 0,
  1142. class: "expand",
  1143. onClick: _cache[0] || (_cache[0] = ($event) => {
  1144. mask.value = false;
  1145. handOpen.value = true;
  1146. })
  1147. }, "展开")) : vue.createCommentVNode("", true)
  1148. ])) : vue.createCommentVNode("", true);
  1149. };
  1150. }
  1151. };
  1152. const BaseHtmlRender = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["__scopeId", "data-v-a77c3d96"]]);
  1153. const _sfc_main$a = {
  1154. name: "Comment",
  1155. components: { BaseHtmlRender, Author, PostEditor, Point },
  1156. inject: ["post", "postDetailWidth", "show"],
  1157. props: {
  1158. modelValue: {
  1159. reply_content: ""
  1160. },
  1161. type: {
  1162. type: String,
  1163. default() {
  1164. return "list";
  1165. }
  1166. }
  1167. },
  1168. data() {
  1169. return {
  1170. edit: false,
  1171. ding: false,
  1172. expand: true,
  1173. expandWrong: false,
  1174. replyInfo: `@${this.modelValue.username} #${this.modelValue.floor} `,
  1175. cssStyle: null,
  1176. floor: this.modelValue.floor
  1177. };
  1178. },
  1179. watch: {
  1180. show(e) {
  1181. if (e) {
  1182. this.edit = false;
  1183. }
  1184. }
  1185. },
  1186. computed: {
  1187. myClass() {
  1188. return {
  1189. isOp: this.modelValue.isOp,
  1190. ding: this.ding,
  1191. isLevelOne: this.modelValue.level === 0,
  1192. ["c_" + this.floor]: this.type !== "top"
  1193. };
  1194. }
  1195. },
  1196. created() {
  1197. },
  1198. mounted() {
  1199. let rect = this.$refs.comment.getBoundingClientRect();
  1200. let ban = this.postDetailWidth / 2;
  1201. if (ban < rect.width && rect.width < ban + 25 && this.modelValue.children.length) {
  1202. this.expand = false;
  1203. let padding = 2;
  1204. this.cssStyle = {
  1205. padding: "1rem 0",
  1206. width: `calc(${this.postDetailWidth}px - ${padding}rem)`,
  1207. transform: `translateX(calc(${rect.width - this.postDetailWidth}px + ${padding}rem))`
  1208. };
  1209. }
  1210. },
  1211. methods: {
  1212. //高亮一下
  1213. showDing() {
  1214. this.ding = true;
  1215. setTimeout(() => {
  1216. this.ding = false;
  1217. }, 2e3);
  1218. },
  1219. hide() {
  1220. let url = `${window.baseUrl}/ignore/reply/${this.modelValue.id}?once=${this.post.once}`;
  1221. eventBus.emit(CMD$1.REMOVE, this.modelValue.floor);
  1222. $.post(url).then((res) => {
  1223. eventBus.emit(CMD$1.REFRESH_ONCE);
  1224. eventBus.emit(CMD$1.SHOW_MSG, { type: "success", text: "隐藏成功" });
  1225. }, (err) => {
  1226. eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "隐藏成功,仅本次有效(接口调用失败!)" });
  1227. });
  1228. },
  1229. toggle() {
  1230. this.expand = !this.expand;
  1231. }
  1232. }
  1233. };
  1234. const _withScopeId$6 = (n) => (vue.pushScopeId("data-v-cb22281c"), n = n(), vue.popScopeId(), n);
  1235. const _hoisted_1$a = ["data-floor"];
  1236. const _hoisted_2$8 = { class: "comment-content" };
  1237. const _hoisted_3$6 = { class: "right" };
  1238. const _hoisted_4$4 = { class: "w" };
  1239. const _hoisted_5$4 = {
  1240. key: 0,
  1241. class: "wrong-wrapper"
  1242. };
  1243. const _hoisted_6$4 = ["href"];
  1244. const _hoisted_7$3 = { class: "del-line" };
  1245. const _hoisted_8$3 = /* @__PURE__ */ _withScopeId$6(() => /* @__PURE__ */ vue.createElementVNode("i", {
  1246. class: "fa fa-question-circle-o wrong-icon",
  1247. "aria-hidden": "true"
  1248. }, null, -1));
  1249. const _hoisted_9$3 = {
  1250. key: 0,
  1251. class: "warning"
  1252. };
  1253. const _hoisted_10$3 = /* @__PURE__ */ _withScopeId$6(() => /* @__PURE__ */ vue.createElementVNode("br", null, null, -1));
  1254. const _hoisted_11$3 = /* @__PURE__ */ _withScopeId$6(() => /* @__PURE__ */ vue.createElementVNode("br", null, null, -1));
  1255. const _hoisted_12$3 = /* @__PURE__ */ _withScopeId$6(() => /* @__PURE__ */ vue.createElementVNode("br", null, null, -1));
  1256. const _hoisted_13$3 = /* @__PURE__ */ _withScopeId$6(() => /* @__PURE__ */ vue.createElementVNode("br", null, null, -1));
  1257. const _hoisted_14$3 = /* @__PURE__ */ _withScopeId$6(() => /* @__PURE__ */ vue.createElementVNode("br", null, null, -1));
  1258. const _hoisted_15$3 = /* @__PURE__ */ _withScopeId$6(() => /* @__PURE__ */ vue.createElementVNode("a", {
  1259. href: "https://github.com/zyronon/v2ex-script/discussions/7",
  1260. target: "_blank"
  1261. }, "这里", -1));
  1262. function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
  1263. const _component_Author = vue.resolveComponent("Author");
  1264. const _component_BaseHtmlRender = vue.resolveComponent("BaseHtmlRender");
  1265. const _component_PostEditor = vue.resolveComponent("PostEditor");
  1266. const _component_Comment = vue.resolveComponent("Comment", true);
  1267. return vue.openBlock(), vue.createElementBlock("div", {
  1268. class: vue.normalizeClass(["comment", $options.myClass]),
  1269. ref: "comment",
  1270. "data-floor": $data.floor
  1271. }, [
  1272. vue.createVNode(_component_Author, {
  1273. modelValue: $data.expand,
  1274. "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.expand = $event),
  1275. comment: $props.modelValue,
  1276. onReply: _cache[1] || (_cache[1] = ($event) => $data.edit = !$data.edit),
  1277. type: $props.type,
  1278. onHide: $options.hide
  1279. }, null, 8, ["modelValue", "comment", "type", "onHide"]),
  1280. $data.cssStyle && !$data.expand ? (vue.openBlock(), vue.createElementBlock("div", {
  1281. key: 0,
  1282. class: "more ago",
  1283. onClick: _cache[2] || (_cache[2] = ($event) => $data.expand = !$data.expand)
  1284. }, " 由于嵌套回复层级太深,自动将后续回复隐藏 ")) : vue.createCommentVNode("", true),
  1285. vue.withDirectives(vue.createElementVNode("div", {
  1286. class: "comment-content-w",
  1287. style: vue.normalizeStyle($data.cssStyle)
  1288. }, [
  1289. $data.cssStyle ? (vue.openBlock(), vue.createElementBlock("div", {
  1290. key: 0,
  1291. class: "more ago",
  1292. onClick: _cache[3] || (_cache[3] = ($event) => $data.expand = !$data.expand)
  1293. }, " 由于嵌套回复层级太深,自动将以下回复移至可见范围 ")) : vue.createCommentVNode("", true),
  1294. vue.createElementVNode("div", _hoisted_2$8, [
  1295. vue.createElementVNode("div", {
  1296. class: "left expand-line",
  1297. onClick: _cache[4] || (_cache[4] = (...args) => $options.toggle && $options.toggle(...args))
  1298. }),
  1299. vue.createElementVNode("div", _hoisted_3$6, [
  1300. vue.createElementVNode("div", _hoisted_4$4, [
  1301. $props.modelValue.isWrong ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5$4, [
  1302. vue.createElementVNode("span", {
  1303. onClick: _cache[5] || (_cache[5] = ($event) => $data.expandWrong = !$data.expandWrong),
  1304. title: "点击楼层号查看提示"
  1305. }, [
  1306. vue.createElementVNode("a", {
  1307. href: "/member/" + $props.modelValue.replyUsers[0]
  1308. }, "@" + vue.toDisplayString($props.modelValue.replyUsers[0]) + "  ", 9, _hoisted_6$4),
  1309. vue.createElementVNode("span", _hoisted_7$3, "#" + vue.toDisplayString($props.modelValue.replyFloor), 1),
  1310. _hoisted_8$3
  1311. ]),
  1312. $data.expandWrong ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9$3, [
  1313. vue.createTextVNode(" 这条回复似乎有点问题,指定的楼层号与@的人对应不上 "),
  1314. _hoisted_10$3,
  1315. vue.createTextVNode(" 原因可能有下面几种: "),
  1316. _hoisted_11$3,
  1317. vue.createTextVNode(" 一、屏蔽用户导致楼层塌陷:你屏蔽了A,自A以后的回复的楼层号都会减1 "),
  1318. _hoisted_12$3,
  1319. vue.createTextVNode(" 二、忽略回复导致楼层塌陷:原理同上 "),
  1320. _hoisted_13$3,
  1321. vue.createTextVNode(" 三、层主回复时指定错了楼层号(同一,层主屏蔽了别人,导致楼层塌陷) "),
  1322. _hoisted_14$3,
  1323. vue.createTextVNode(" 四、脚本解析错误,请在"),
  1324. _hoisted_15$3,
  1325. vue.createTextVNode("反馈 ")
  1326. ])) : vue.createCommentVNode("", true)
  1327. ])) : vue.createCommentVNode("", true),
  1328. vue.createVNode(_component_BaseHtmlRender, {
  1329. class: "reply_content",
  1330. html: $props.modelValue.reply_content
  1331. }, null, 8, ["html"]),
  1332. $data.edit ? (vue.openBlock(), vue.createBlock(_component_PostEditor, {
  1333. key: 1,
  1334. onClose: _cache[6] || (_cache[6] = ($event) => $data.edit = false),
  1335. replyInfo: $data.replyInfo,
  1336. replyUser: $props.modelValue.username,
  1337. replyFloor: $props.modelValue.floor
  1338. }, null, 8, ["replyInfo", "replyUser", "replyFloor"])) : vue.createCommentVNode("", true)
  1339. ]),
  1340. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($props.modelValue.children, (item, index2) => {
  1341. return vue.openBlock(), vue.createBlock(_component_Comment, {
  1342. modelValue: $props.modelValue.children[index2],
  1343. "onUpdate:modelValue": ($event) => $props.modelValue.children[index2] = $event,
  1344. key: index2
  1345. }, null, 8, ["modelValue", "onUpdate:modelValue"]);
  1346. }), 128))
  1347. ])
  1348. ]),
  1349. $data.cssStyle ? (vue.openBlock(), vue.createElementBlock("div", {
  1350. key: 1,
  1351. class: "more ago",
  1352. onClick: _cache[7] || (_cache[7] = ($event) => $data.expand = !$data.expand)
  1353. }, " 由于嵌套回复层级太深,自动将以上回复移至可见范围 ")) : vue.createCommentVNode("", true)
  1354. ], 4), [
  1355. [vue.vShow, $data.expand]
  1356. ])
  1357. ], 10, _hoisted_1$a);
  1358. }
  1359. const Comment = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["render", _sfc_render$6], ["__scopeId", "data-v-cb22281c"]]);
  1360. const _sfc_main$9 = {
  1361. name: "Toolbar",
  1362. inject: [
  1363. "isLogin",
  1364. "post",
  1365. "pageType"
  1366. ],
  1367. data() {
  1368. return {
  1369. timer: null,
  1370. loading: false,
  1371. loading2: false,
  1372. loading3: false
  1373. };
  1374. },
  1375. methods: {
  1376. checkIsLogin(emitName = "") {
  1377. if (!this.isLogin) {
  1378. eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "请先登录!" });
  1379. return false;
  1380. }
  1381. this.$emit(emitName);
  1382. return true;
  1383. },
  1384. getColor(val) {
  1385. return val ? "#ff4500" : "#929596";
  1386. },
  1387. getIsFull(val) {
  1388. return val ? "#ff4500" : "none";
  1389. },
  1390. tweet() {
  1391. if (!this.checkIsLogin())
  1392. return;
  1393. let username = window.user.username;
  1394. let url = `https://twitter.com/intent/tweet?url=${window.baseUrl}/t/${this.post.id}?r=${username}&related=v2ex&text=${this.post.title}`;
  1395. window.win().open(url, "_blank", "width=550,height=370");
  1396. },
  1397. report() {
  1398. if (!this.checkIsLogin())
  1399. return;
  1400. if (!this.isLogin)
  1401. return;
  1402. if (this.post.isReport)
  1403. return;
  1404. let username = window.user.username;
  1405. let url = `https://twitter.com/share?url=${window.baseUrl}/t/${this.post.id}?r=${username}&amp;related=v2ex&amp;hashtags=apple&amp;text=${this.post.title}`;
  1406. window.win().open(url, "_blank", "width=550,height=370");
  1407. },
  1408. async toggleIgnore() {
  1409. if (!this.checkIsLogin())
  1410. return;
  1411. let url = `${window.baseUrl}/${this.post.isIgnore ? "unignore" : "ignore"}/topic/${this.post.id}?once=${this.post.once}`;
  1412. if (this.pageType === "post") {
  1413. this.loading2 = true;
  1414. let apiRes = await window.win().fetch(url);
  1415. if (apiRes.redirected) {
  1416. if (!this.post.isIgnore) {
  1417. window.win().location = window.baseUrl;
  1418. }
  1419. eventBus.emit(CMD$1.SHOW_MSG, { type: "success", text: this.post.isIgnore ? "取消成功" : "忽略成功" });
  1420. eventBus.emit(CMD$1.MERGE, { isIgnore: !this.post.isIgnore });
  1421. } else {
  1422. eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "忽略失败" });
  1423. }
  1424. this.loading2 = false;
  1425. } else {
  1426. if (this.post.isIgnore) {
  1427. this.loading2 = true;
  1428. } else {
  1429. eventBus.emit(CMD$1.IGNORE);
  1430. }
  1431. let apiRes = await window.win().fetch(url);
  1432. if (apiRes.redirected) {
  1433. if (this.post.isIgnore) {
  1434. eventBus.emit(CMD$1.REFRESH_ONCE);
  1435. }
  1436. eventBus.emit(CMD$1.SHOW_MSG, { type: "success", text: this.post.isIgnore ? "取消成功" : "忽略成功" });
  1437. eventBus.emit(CMD$1.MERGE, { isIgnore: !this.post.isIgnore });
  1438. } else {
  1439. eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "忽略成功,仅本次有效(接口调用失败!)" });
  1440. }
  1441. this.loading2 = false;
  1442. }
  1443. },
  1444. async toggleFavorite() {
  1445. if (!this.checkIsLogin())
  1446. return;
  1447. this.loading = true;
  1448. let url = `${window.baseUrl}/${this.post.isFavorite ? "unfavorite" : "favorite"}/topic/${this.post.id}?once=${this.post.once}`;
  1449. let apiRes = await window.win().fetch(url);
  1450. this.loading = false;
  1451. if (apiRes.redirected) {
  1452. let htmlText = await apiRes.text();
  1453. if (htmlText.search(this.post.isFavorite ? "加入收藏" : "取消收藏")) {
  1454. eventBus.emit(CMD$1.MERGE, { collectCount: this.post.isFavorite ? this.post.collectCount - 1 : this.post.collectCount + 1 });
  1455. eventBus.emit(CMD$1.SHOW_MSG, { type: "success", text: this.post.isFavorite ? "取消成功" : "收藏成功" });
  1456. eventBus.emit(CMD$1.REFRESH_ONCE, htmlText);
  1457. eventBus.emit(CMD$1.MERGE, { isFavorite: !this.post.isFavorite });
  1458. return;
  1459. }
  1460. }
  1461. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "操作失败" });
  1462. }
  1463. }
  1464. };
  1465. const _withScopeId$5 = (n) => (vue.pushScopeId("data-v-0f5085fc"), n = n(), vue.popScopeId(), n);
  1466. const _hoisted_1$9 = { class: "toolbar" };
  1467. const _hoisted_2$7 = /* @__PURE__ */ vue.createStaticVNode('<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" data-v-0f5085fc><path d="M4 6H44V36H29L24 41L19 36H4V6Z" fill="none" stroke="#929596" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-v-0f5085fc></path><path d="M23 21H25.0025" stroke="#929596" stroke-width="2" stroke-linecap="round" data-v-0f5085fc></path><path d="M33.001 21H34.9999" stroke="#929596" stroke-width="2" stroke-linecap="round" data-v-0f5085fc></path><path d="M13.001 21H14.9999" stroke="#929596" stroke-width="2" stroke-linecap="round" data-v-0f5085fc></path></svg><span data-v-0f5085fc>回复</span>', 2);
  1468. const _hoisted_4$3 = [
  1469. _hoisted_2$7
  1470. ];
  1471. const _hoisted_5$3 = {
  1472. viewBox: "0 0 48 48",
  1473. fill: "none",
  1474. xmlns: "http://www.w3.org/2000/svg"
  1475. };
  1476. const _hoisted_6$3 = ["fill", "stroke"];
  1477. const _hoisted_7$2 = {
  1478. key: 1,
  1479. class: "tool no-hover"
  1480. };
  1481. const _hoisted_8$2 = /* @__PURE__ */ _withScopeId$5(() => /* @__PURE__ */ vue.createElementVNode("svg", {
  1482. viewBox: "0 0 48 48",
  1483. fill: "none",
  1484. xmlns: "http://www.w3.org/2000/svg"
  1485. }, [
  1486. /* @__PURE__ */ vue.createElementVNode("path", {
  1487. d: "M28 6H42V20",
  1488. stroke: "#929596",
  1489. "stroke-width": "2",
  1490. "stroke-linecap": "round",
  1491. "stroke-linejoin": "round"
  1492. }),
  1493. /* @__PURE__ */ vue.createElementVNode("path", {
  1494. d: "M42 29.4737V39C42 40.6569 40.6569 42 39 42H9C7.34315 42 6 40.6569 6 39V9C6 7.34315 7.34315 6 9 6L18 6",
  1495. stroke: "#929596",
  1496. "stroke-width": "2",
  1497. "stroke-linecap": "round",
  1498. "stroke-linejoin": "round"
  1499. }),
  1500. /* @__PURE__ */ vue.createElementVNode("path", {
  1501. d: "M25.7998 22.1999L41.0998 6.8999",
  1502. stroke: "#929596",
  1503. "stroke-width": "2",
  1504. "stroke-linecap": "round",
  1505. "stroke-linejoin": "round"
  1506. })
  1507. ], -1));
  1508. const _hoisted_9$2 = /* @__PURE__ */ _withScopeId$5(() => /* @__PURE__ */ vue.createElementVNode("span", null, "Tweet", -1));
  1509. const _hoisted_10$2 = [
  1510. _hoisted_8$2,
  1511. _hoisted_9$2
  1512. ];
  1513. const _hoisted_11$2 = {
  1514. viewBox: "0 0 48 48",
  1515. fill: "none",
  1516. xmlns: "http://www.w3.org/2000/svg"
  1517. };
  1518. const _hoisted_12$2 = ["fill", "stroke"];
  1519. const _hoisted_13$2 = ["fill", "stroke"];
  1520. const _hoisted_14$2 = ["fill", "stroke"];
  1521. const _hoisted_15$2 = /* @__PURE__ */ vue.createStaticVNode('<svg width="19" height="19" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" data-v-0f5085fc><path d="M36 35H12V21C12 14.3726 17.3726 9 24 9C30.6274 9 36 14.3726 36 21V35Z" fill="#929596" stroke="#929596" stroke-width="4" stroke-linejoin="round" data-v-0f5085fc></path><path d="M8 42H40" stroke="#929596" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" data-v-0f5085fc></path><path d="M4 13L7 14" stroke="#929596" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" data-v-0f5085fc></path><path d="M13 3.9999L14 6.9999" stroke="#929596" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" data-v-0f5085fc></path><path d="M10.0001 9.99989L7.00009 6.99989" stroke="#929596" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" data-v-0f5085fc></path></svg>', 1);
  1522. function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
  1523. return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$9, [
  1524. vue.createElementVNode("div", {
  1525. class: "tool",
  1526. onClick: _cache[0] || (_cache[0] = ($event) => $options.checkIsLogin("reply"))
  1527. }, _hoisted_4$3),
  1528. $options.post.once ? (vue.openBlock(), vue.createElementBlock("div", {
  1529. key: 0,
  1530. class: vue.normalizeClass(["tool", { loading: $data.loading }]),
  1531. onClick: _cache[1] || (_cache[1] = (...args) => $options.toggleFavorite && $options.toggleFavorite(...args))
  1532. }, [
  1533. (vue.openBlock(), vue.createElementBlock("svg", _hoisted_5$3, [
  1534. vue.createElementVNode("path", {
  1535. d: "M23.9986 5L17.8856 17.4776L4 19.4911L14.0589 29.3251L11.6544 43L23.9986 36.4192L36.3454 43L33.9586 29.3251L44 19.4911L30.1913 17.4776L23.9986 5Z",
  1536. fill: $options.getIsFull($options.post.isFavorite),
  1537. stroke: $options.getColor($options.post.isFavorite),
  1538. "stroke-width": "2",
  1539. "stroke-linejoin": "round"
  1540. }, null, 8, _hoisted_6$3)
  1541. ])),
  1542. vue.createElementVNode("span", null, vue.toDisplayString($options.post.isFavorite ? "取消收藏" : "加入收藏"), 1)
  1543. ], 2)) : vue.createCommentVNode("", true),
  1544. $options.post.once && $options.post.collectCount !== 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_7$2, [
  1545. vue.createElementVNode("span", null, vue.toDisplayString($options.post.collectCount + "人收藏"), 1)
  1546. ])) : vue.createCommentVNode("", true),
  1547. vue.createElementVNode("div", {
  1548. class: "tool",
  1549. onClick: _cache[2] || (_cache[2] = (...args) => $options.tweet && $options.tweet(...args))
  1550. }, _hoisted_10$2),
  1551. $options.post.once ? (vue.openBlock(), vue.createElementBlock("div", {
  1552. key: 2,
  1553. class: vue.normalizeClass(["tool", { "loading": $data.loading2 }]),
  1554. onClick: _cache[3] || (_cache[3] = (...args) => $options.toggleIgnore && $options.toggleIgnore(...args))
  1555. }, [
  1556. (vue.openBlock(), vue.createElementBlock("svg", _hoisted_11$2, [
  1557. vue.createElementVNode("path", {
  1558. fill: $options.getIsFull($options.post.isIgnore),
  1559. stroke: $options.getColor($options.post.isIgnore),
  1560. d: "M9.85786 18C6.23858 21 4 24 4 24C4 24 12.9543 36 24 36C25.3699 36 26.7076 35.8154 28 35.4921M20.0318 12.5C21.3144 12.1816 22.6414 12 24 12C35.0457 12 44 24 44 24C44 24 41.7614 27 38.1421 30",
  1561. "stroke-width": "2",
  1562. "stroke-linecap": "round",
  1563. "stroke-linejoin": "round"
  1564. }, null, 8, _hoisted_12$2),
  1565. vue.createElementVNode("path", {
  1566. fill: $options.getIsFull($options.post.isIgnore),
  1567. d: "M20.3142 20.6211C19.4981 21.5109 19 22.6972 19 23.9998C19 26.7612 21.2386 28.9998 24 28.9998C25.3627 28.9998 26.5981 28.4546 27.5 27.5705",
  1568. stroke: $options.getColor($options.post.isIgnore),
  1569. "stroke-width": "2",
  1570. "stroke-linecap": "round",
  1571. "stroke-linejoin": "round"
  1572. }, null, 8, _hoisted_13$2),
  1573. vue.createElementVNode("path", {
  1574. d: "M42 42L6 6",
  1575. fill: $options.getIsFull($options.post.isIgnore),
  1576. stroke: $options.getColor($options.post.isIgnore),
  1577. "stroke-width": "2",
  1578. "stroke-linecap": "round",
  1579. "stroke-linejoin": "round"
  1580. }, null, 8, _hoisted_14$2)
  1581. ])),
  1582. vue.createElementVNode("span", null, vue.toDisplayString($options.post.isIgnore ? "取消忽略" : "忽略主题"), 1)
  1583. ], 2)) : vue.createCommentVNode("", true),
  1584. $options.post.once && $options.post.isLogin ? (vue.openBlock(), vue.createElementBlock("div", {
  1585. key: 3,
  1586. class: vue.normalizeClass(["tool", { "loading": $data.loading3, "no-hover": $options.post.isLogin }]),
  1587. onClick: _cache[4] || (_cache[4] = (...args) => $options.report && $options.report(...args))
  1588. }, [
  1589. _hoisted_15$2,
  1590. vue.createElementVNode("span", null, vue.toDisplayString($options.post.isReport ? "你已对本主题进行了报告" : "报告这个主题"), 1)
  1591. ], 2)) : vue.createCommentVNode("", true)
  1592. ]);
  1593. }
  1594. const Toolbar = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["render", _sfc_render$5], ["__scopeId", "data-v-0f5085fc"]]);
  1595. const _sfc_main$8 = {
  1596. name: "Tooltip",
  1597. props: {
  1598. title: {
  1599. type: String,
  1600. default() {
  1601. return "";
  1602. }
  1603. }
  1604. },
  1605. data() {
  1606. return {
  1607. show: false
  1608. };
  1609. },
  1610. methods: {
  1611. hoverIn(e) {
  1612. let rect = e.target.getBoundingClientRect();
  1613. this.show = true;
  1614. vue.nextTick(() => {
  1615. let tip = this.$refs.tip.getBoundingClientRect();
  1616. this.$refs.tip.style.top = rect.top - tip.height - 5 + "px";
  1617. this.$refs.tip.style.left = rect.left + rect.width / 2 + "px";
  1618. });
  1619. }
  1620. }
  1621. };
  1622. const _hoisted_1$8 = { class: "pop-confirm" };
  1623. function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
  1624. return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$8, [
  1625. (vue.openBlock(), vue.createBlock(vue.Teleport, { to: "body" }, [
  1626. vue.createVNode(vue.Transition, null, {
  1627. default: vue.withCtx(() => [
  1628. $data.show ? (vue.openBlock(), vue.createElementBlock("div", {
  1629. key: 0,
  1630. class: "tip",
  1631. ref: "tip"
  1632. }, vue.toDisplayString($props.title), 513)) : vue.createCommentVNode("", true)
  1633. ]),
  1634. _: 1
  1635. })
  1636. ])),
  1637. vue.createElementVNode("span", {
  1638. onMouseenter: _cache[0] || (_cache[0] = (...args) => $options.hoverIn && $options.hoverIn(...args)),
  1639. onMouseleave: _cache[1] || (_cache[1] = ($event) => $data.show = false)
  1640. }, [
  1641. vue.renderSlot(_ctx.$slots, "default")
  1642. ], 32)
  1643. ]);
  1644. }
  1645. const Tooltip = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$4]]);
  1646. const _withScopeId$4 = (n) => (vue.pushScopeId("data-v-3eb530b9"), n = n(), vue.popScopeId(), n);
  1647. const _hoisted_1$7 = {
  1648. class: "comment",
  1649. ref: "comment"
  1650. };
  1651. const _hoisted_2$6 = ["href"];
  1652. const _hoisted_3$5 = ["src"];
  1653. const _hoisted_4$2 = { class: "texts" };
  1654. const _hoisted_5$2 = {
  1655. key: 0,
  1656. class: "point"
  1657. };
  1658. const _hoisted_6$2 = /* @__PURE__ */ _withScopeId$4(() => /* @__PURE__ */ vue.createElementVNode("svg", {
  1659. width: "19",
  1660. height: "19",
  1661. viewBox: "0 0 48 48",
  1662. fill: "none"
  1663. }, [
  1664. /* @__PURE__ */ vue.createElementVNode("path", {
  1665. d: "M15 8C8.92487 8 4 12.9249 4 19C4 30 17 40 24 42.3262C31 40 44 30 44 19C44 12.9249 39.0751 8 33 8C29.2797 8 25.9907 9.8469 24 12.6738C22.0093 9.8469 18.7203 8 15 8Z",
  1666. fill: "#E02A2A",
  1667. stroke: "#E02A2A",
  1668. "stroke-width": "2",
  1669. "stroke-linecap": "round",
  1670. "stroke-linejoin": "round"
  1671. })
  1672. ], -1));
  1673. const _hoisted_7$1 = { class: "num" };
  1674. const _hoisted_8$1 = { class: "my-tag" };
  1675. const _hoisted_9$1 = /* @__PURE__ */ _withScopeId$4(() => /* @__PURE__ */ vue.createElementVNode("i", { class: "fa fa-tag" }, null, -1));
  1676. const _hoisted_10$1 = {
  1677. key: 2,
  1678. class: "ago"
  1679. };
  1680. const _hoisted_11$1 = {
  1681. key: 3,
  1682. class: "mod"
  1683. };
  1684. const _hoisted_12$1 = {
  1685. key: 4,
  1686. class: "op"
  1687. };
  1688. const _hoisted_13$1 = ["href"];
  1689. const _hoisted_14$1 = {
  1690. key: 5,
  1691. class: "op"
  1692. };
  1693. const _hoisted_15$1 = {
  1694. key: 6,
  1695. class: "mod"
  1696. };
  1697. const _hoisted_16$1 = {
  1698. key: 7,
  1699. class: "ago"
  1700. };
  1701. const _hoisted_17$1 = { class: "my-tag" };
  1702. const _hoisted_18$1 = /* @__PURE__ */ _withScopeId$4(() => /* @__PURE__ */ vue.createElementVNode("i", { class: "fa fa-tag" }, null, -1));
  1703. const _hoisted_19$1 = {
  1704. key: 9,
  1705. class: "point"
  1706. };
  1707. const _hoisted_20$1 = /* @__PURE__ */ _withScopeId$4(() => /* @__PURE__ */ vue.createElementVNode("svg", {
  1708. width: "19",
  1709. height: "19",
  1710. viewBox: "0 0 48 48",
  1711. fill: "none"
  1712. }, [
  1713. /* @__PURE__ */ vue.createElementVNode("path", {
  1714. d: "M15 8C8.92487 8 4 12.9249 4 19C4 30 17 40 24 42.3262C31 40 44 30 44 19C44 12.9249 39.0751 8 33 8C29.2797 8 25.9907 9.8469 24 12.6738C22.0093 9.8469 18.7203 8 15 8Z",
  1715. fill: "#E02A2A",
  1716. stroke: "#E02A2A",
  1717. "stroke-width": "2",
  1718. "stroke-linecap": "round",
  1719. "stroke-linejoin": "round"
  1720. })
  1721. ], -1));
  1722. const _hoisted_21$1 = { class: "num" };
  1723. const _hoisted_22$1 = ["href"];
  1724. const _hoisted_23$1 = ["src"];
  1725. const _hoisted_24$1 = { class: "Author-right" };
  1726. const _hoisted_25$1 = { class: "floor" };
  1727. const _hoisted_26$1 = /* @__PURE__ */ _withScopeId$4(() => /* @__PURE__ */ vue.createElementVNode("span", null, "跳转", -1));
  1728. const _hoisted_27$1 = [
  1729. _hoisted_26$1
  1730. ];
  1731. const _sfc_main$7 = {
  1732. __name: "SingleComment",
  1733. props: {
  1734. comment: {
  1735. reply_content: ""
  1736. },
  1737. isRight: {
  1738. type: Boolean,
  1739. default() {
  1740. return false;
  1741. }
  1742. }
  1743. },
  1744. setup(__props) {
  1745. const props = __props;
  1746. const config = vue.inject("config");
  1747. const isLogin = vue.inject("isLogin");
  1748. const tags = vue.inject("tags");
  1749. const myTags = vue.computed(() => {
  1750. return tags[props.comment.username] ?? [];
  1751. });
  1752. function jump() {
  1753. eventBus.emit(CMD$1.JUMP, props.comment.floor);
  1754. }
  1755. return (_ctx, _cache) => {
  1756. return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$7, [
  1757. !__props.isRight ? (vue.openBlock(), vue.createElementBlock("a", {
  1758. key: 0,
  1759. class: "avatar",
  1760. href: `/member/${__props.comment.username}`
  1761. }, [
  1762. vue.createElementVNode("img", {
  1763. src: __props.comment.avatar,
  1764. alt: ""
  1765. }, null, 8, _hoisted_3$5)
  1766. ], 8, _hoisted_2$6)) : vue.createCommentVNode("", true),
  1767. vue.createElementVNode("div", {
  1768. class: vue.normalizeClass(["comment-body", { isRight: __props.isRight }])
  1769. }, [
  1770. vue.createElementVNode("div", _hoisted_4$2, [
  1771. __props.comment.thankCount && __props.isRight ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5$2, [
  1772. _hoisted_6$2,
  1773. vue.createElementVNode("div", _hoisted_7$1, vue.toDisplayString(__props.comment.thankCount), 1)
  1774. ])) : vue.createCommentVNode("", true),
  1775. vue.unref(isLogin) && vue.unref(config).openTag && __props.isRight ? (vue.openBlock(true), vue.createElementBlock(vue.Fragment, { key: 1 }, vue.renderList(vue.unref(myTags), (i) => {
  1776. return vue.openBlock(), vue.createElementBlock("span", _hoisted_8$1, [
  1777. _hoisted_9$1,
  1778. vue.createElementVNode("span", null, vue.toDisplayString(i), 1)
  1779. ]);
  1780. }), 256)) : vue.createCommentVNode("", true),
  1781. __props.isRight ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_10$1, vue.toDisplayString(__props.comment.date), 1)) : vue.createCommentVNode("", true),
  1782. __props.comment.isMod && __props.isRight ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11$1, "MOD")) : vue.createCommentVNode("", true),
  1783. __props.comment.isOp && __props.isRight ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_12$1, "OP")) : vue.createCommentVNode("", true),
  1784. vue.createElementVNode("a", {
  1785. href: `/member/${__props.comment.username}`,
  1786. class: "username"
  1787. }, vue.toDisplayString(__props.comment.username), 9, _hoisted_13$1),
  1788. __props.comment.isOp && !__props.isRight ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_14$1, "OP")) : vue.createCommentVNode("", true),
  1789. __props.comment.isMod && !__props.isRight ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_15$1, "MOD")) : vue.createCommentVNode("", true),
  1790. !__props.isRight ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_16$1, vue.toDisplayString(__props.comment.date), 1)) : vue.createCommentVNode("", true),
  1791. vue.unref(isLogin) && vue.unref(config).openTag && !__props.isRight ? (vue.openBlock(true), vue.createElementBlock(vue.Fragment, { key: 8 }, vue.renderList(vue.unref(myTags), (i) => {
  1792. return vue.openBlock(), vue.createElementBlock("span", _hoisted_17$1, [
  1793. _hoisted_18$1,
  1794. vue.createElementVNode("span", null, vue.toDisplayString(i), 1)
  1795. ]);
  1796. }), 256)) : vue.createCommentVNode("", true),
  1797. __props.comment.thankCount && !__props.isRight ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_19$1, [
  1798. _hoisted_20$1,
  1799. vue.createElementVNode("div", _hoisted_21$1, vue.toDisplayString(__props.comment.thankCount), 1)
  1800. ])) : vue.createCommentVNode("", true)
  1801. ]),
  1802. vue.createVNode(vue.unref(BaseHtmlRender), {
  1803. class: "reply_content",
  1804. html: __props.comment.reply_content
  1805. }, null, 8, ["html"])
  1806. ], 2),
  1807. __props.isRight ? (vue.openBlock(), vue.createElementBlock("a", {
  1808. key: 1,
  1809. class: "avatar",
  1810. href: `/member/${__props.comment.username}`
  1811. }, [
  1812. vue.createElementVNode("img", {
  1813. src: __props.comment.avatar,
  1814. alt: ""
  1815. }, null, 8, _hoisted_23$1)
  1816. ], 8, _hoisted_22$1)) : vue.createCommentVNode("", true),
  1817. vue.createElementVNode("div", _hoisted_24$1, [
  1818. vue.createElementVNode("div", _hoisted_25$1, vue.toDisplayString(__props.comment.floor), 1),
  1819. vue.createElementVNode("div", {
  1820. class: "tool jump",
  1821. onClick: jump
  1822. }, _hoisted_27$1)
  1823. ])
  1824. ], 512);
  1825. };
  1826. }
  1827. };
  1828. const SingleComment = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["__scopeId", "data-v-3eb530b9"]]);
  1829. function debounce(fn, delay, scope) {
  1830. let timer = null;
  1831. return function() {
  1832. let context = scope || this, args = arguments;
  1833. clearTimeout(timer);
  1834. timer = setTimeout(function() {
  1835. fn.apply(context, args);
  1836. timer = null;
  1837. }, delay);
  1838. };
  1839. }
  1840. const _sfc_main$6 = {
  1841. name: "detail",
  1842. components: {
  1843. SingleComment,
  1844. PopConfirm,
  1845. Comment,
  1846. PostEditor,
  1847. Point,
  1848. Toolbar,
  1849. BaseHtmlRender,
  1850. Tooltip
  1851. },
  1852. inject: ["allReplyUsers", "post", "isLogin", "config", "pageType", "isNight", "showConfig"],
  1853. provide() {
  1854. return {
  1855. postDetailWidth: vue.computed(() => {
  1856. var _a;
  1857. return ((_a = this.$refs.comments) == null ? void 0 : _a.getBoundingClientRect().width) || 0;
  1858. })
  1859. };
  1860. },
  1861. props: {
  1862. modelValue: {
  1863. type: Boolean,
  1864. default() {
  1865. return false;
  1866. }
  1867. },
  1868. loading: {
  1869. type: Boolean,
  1870. default() {
  1871. return false;
  1872. }
  1873. },
  1874. displayType: 0
  1875. },
  1876. data() {
  1877. return {
  1878. isSticky: false,
  1879. selectCallIndex: 0,
  1880. postDetailWidth: 0,
  1881. showCallList: false,
  1882. showRelationReply: false,
  1883. replyText: "",
  1884. callStyle: {
  1885. top: 0,
  1886. left: 0
  1887. },
  1888. targetUser: {
  1889. left: [],
  1890. right: "",
  1891. rightFloor: -1
  1892. },
  1893. debounceScroll: () => {
  1894. },
  1895. read: {
  1896. floor: 0,
  1897. total: 0
  1898. },
  1899. currentFloor: 1
  1900. };
  1901. },
  1902. computed: {
  1903. isPost() {
  1904. return this.pageType === PageType.Post;
  1905. },
  1906. filterCallList() {
  1907. if (this.showCallList) {
  1908. let list = ["管理员", "所有人"].concat(this.allReplyUsers);
  1909. if (this.replyText)
  1910. return list.filter((i) => i.search(this.replyText) > -1);
  1911. return list;
  1912. }
  1913. return [];
  1914. },
  1915. topReply() {
  1916. return this.post.replyList.filter((v) => v.thankCount >= this.config.topReplyLoveMinCount).sort((a, b) => b.thankCount - a.thankCount).slice(0, this.config.topReplyCount);
  1917. },
  1918. replyList() {
  1919. if (this.displayType === 0)
  1920. return this.post.nestedReplies;
  1921. if (this.displayType === 1) {
  1922. return window.clone(this.post.nestedReplies).sort((a, b) => b.thankCount - a.thankCount);
  1923. }
  1924. if (this.displayType === 2)
  1925. return this.post.replyList;
  1926. if (this.displayType === 3)
  1927. return this.post.replyList.filter((v) => {
  1928. var _a;
  1929. return v.username === ((_a = this.post.member) == null ? void 0 : _a.username);
  1930. });
  1931. return [];
  1932. },
  1933. //关联回复
  1934. relationReply() {
  1935. if (this.targetUser.left.length && this.targetUser.right) {
  1936. return this.post.replyList.filter((v) => {
  1937. if (this.targetUser.left.includes(v.username)) {
  1938. if (v.floor > this.targetUser.rightFloor) {
  1939. if (v.replyUsers.includes(this.targetUser.right)) {
  1940. return true;
  1941. }
  1942. } else {
  1943. return true;
  1944. }
  1945. }
  1946. if (v.username === this.targetUser.right) {
  1947. for (let i = 0; i < this.targetUser.left.length; i++) {
  1948. if (v.replyUsers.includes(this.targetUser.left[i])) {
  1949. return true;
  1950. }
  1951. }
  1952. }
  1953. });
  1954. }
  1955. return [];
  1956. }
  1957. },
  1958. watch: {
  1959. "post.id"(n, o) {
  1960. if (this.$refs["post-editor"]) {
  1961. this.$refs["post-editor"].content = "";
  1962. }
  1963. },
  1964. modelValue: {
  1965. handler(newVal) {
  1966. if (this.isPost)
  1967. return;
  1968. if (newVal) {
  1969. document.body.style.overflow = "hidden";
  1970. this.read = this.post.read;
  1971. this.currentFloor = 1;
  1972. vue.nextTick(() => {
  1973. var _a, _b, _c, _d;
  1974. (_b = (_a = this.$refs) == null ? void 0 : _a.main) == null ? void 0 : _b.focus();
  1975. (_d = (_c = this.$refs) == null ? void 0 : _c.detail) == null ? void 0 : _d.scrollTo({ top: 0 });
  1976. });
  1977. } else {
  1978. this.$emit("saveReadList");
  1979. document.body.style.overflow = "unset";
  1980. this.isSticky = false;
  1981. this.showRelationReply = false;
  1982. if ((this.pageType === PageType.Home || this.pageType === PageType.Node) && window.location.pathname !== "/") {
  1983. window.history.back();
  1984. }
  1985. }
  1986. }
  1987. }
  1988. },
  1989. mounted() {
  1990. this.debounceScroll = debounce(this.scroll, 300, false);
  1991. if (this.isLogin) {
  1992. const observer = new IntersectionObserver(
  1993. ([e]) => e.target.toggleAttribute("stuck", e.intersectionRatio < 1),
  1994. { threshold: [1] }
  1995. );
  1996. observer.observe(this.$refs.replyBox);
  1997. window.addEventListener("keydown", this.onKeyDown);
  1998. }
  1999. eventBus.on(CMD$1.SHOW_CALL, (val) => {
  2000. if (val.show) {
  2001. this.showCallList = true;
  2002. this.replyText = val.text;
  2003. if (this.isPost) {
  2004. this.callStyle.top = val.top + $(window.win()).scrollTop() + -40 + "px";
  2005. } else {
  2006. this.callStyle.top = val.top + $(".post-detail").scrollTop() + 15 + "px";
  2007. }
  2008. this.callStyle.left = val.left - $(".main")[0].getBoundingClientRect().left + 10 + "px";
  2009. if (this.selectCallIndex >= this.filterCallList.length) {
  2010. this.selectCallIndex = 0;
  2011. }
  2012. } else {
  2013. this.replyText = "";
  2014. this.showCallList = false;
  2015. this.selectCallIndex = 0;
  2016. }
  2017. });
  2018. eventBus.on(CMD$1.RELATION_REPLY, (val) => {
  2019. this.targetUser = val;
  2020. this.showRelationReply = true;
  2021. });
  2022. eventBus.on(CMD$1.JUMP, this.jump);
  2023. if (this.isPost) {
  2024. window.addEventListener("scroll", this.debounceScroll);
  2025. }
  2026. },
  2027. beforeUnmount() {
  2028. window.removeEventListener("keydown", this.onKeyDown);
  2029. eventBus.off(CMD$1.SHOW_CALL);
  2030. },
  2031. methods: {
  2032. scroll() {
  2033. if (!this.config.rememberLastReadFloor)
  2034. return;
  2035. let height = window.innerHeight * 0.3;
  2036. let comments = $(".comments .comment");
  2037. let forCount = 0;
  2038. for (let i = 0; i < comments.length; i++) {
  2039. forCount++;
  2040. let ins = comments[i];
  2041. let rect = ins.getBoundingClientRect();
  2042. if (rect.top > height) {
  2043. let lastReadFloor = Number(ins.dataset["floor"]);
  2044. console.log("当前阅读楼层", lastReadFloor);
  2045. eventBus.emit(CMD$1.ADD_READ, {
  2046. floor: lastReadFloor > 3 ? lastReadFloor : 0,
  2047. total: this.post.replyList.length
  2048. });
  2049. if (lastReadFloor > 3) {
  2050. this.read.floor = 0;
  2051. }
  2052. break;
  2053. }
  2054. }
  2055. if (forCount === comments.length) {
  2056. console.log("看到最后了");
  2057. eventBus.emit(CMD$1.ADD_READ, {
  2058. floor: forCount,
  2059. total: this.post.replyList.length
  2060. });
  2061. }
  2062. },
  2063. stop(e) {
  2064. },
  2065. jump(floor) {
  2066. try {
  2067. floor = Number(floor);
  2068. } catch (e) {
  2069. }
  2070. if (!floor)
  2071. return;
  2072. if (!this.post.replyList.length) {
  2073. eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "没有回复可跳转!" });
  2074. this.read.floor = 0;
  2075. return;
  2076. }
  2077. if (floor > this.post.replyList.length) {
  2078. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "没有找到对应回复!" });
  2079. this.read.floor = 0;
  2080. return;
  2081. }
  2082. let comment = $(`.c_${floor}`);
  2083. if (!comment.length) {
  2084. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "没有找到对应回复!" });
  2085. this.read.floor = 0;
  2086. return;
  2087. }
  2088. comment[0].scrollIntoView({ behavior: "smooth", block: "center", inline: "center" });
  2089. comment.addClass("ding");
  2090. this.read.floor = 0;
  2091. this.currentFloor = floor + 1;
  2092. setTimeout(() => {
  2093. comment.removeClass("ding");
  2094. }, 2e3);
  2095. },
  2096. jumpLastRead(floor) {
  2097. if (this.config.autoJumpLastReadFloor) {
  2098. if (!floor)
  2099. return;
  2100. setTimeout(() => {
  2101. console.log("上次跳转", floor);
  2102. this.jump(floor);
  2103. eventBus.emit(CMD$1.SHOW_MSG, { type: "success", text: "已跳转到上次阅读位置" });
  2104. }, 300);
  2105. }
  2106. },
  2107. collapseTopReplyList() {
  2108. $(this.$refs.topReply).slideToggle("fast");
  2109. },
  2110. goBottom() {
  2111. this.isSticky = false;
  2112. setTimeout(() => {
  2113. if (this.isPost) {
  2114. let body = $("body , html");
  2115. let scrollHeight = body.prop("scrollHeight");
  2116. body.animate({ scrollTop: scrollHeight - 850 }, 300);
  2117. } else {
  2118. this.$refs.detail.scrollTo({ top: this.$refs.detail.scrollHeight, behavior: "smooth" });
  2119. }
  2120. });
  2121. },
  2122. close(from) {
  2123. if (this.isPost)
  2124. return;
  2125. if (from === "space") {
  2126. if (this.config.closePostDetailBySpace) {
  2127. this.$emit("update:modelValue", false);
  2128. }
  2129. } else {
  2130. this.$emit("update:modelValue", false);
  2131. }
  2132. },
  2133. setCall(e) {
  2134. eventBus.emit(CMD$1.SET_CALL, e);
  2135. this.showCallList = false;
  2136. },
  2137. onKeyDown(e) {
  2138. if (!this.modelValue)
  2139. return;
  2140. if (!this.showCallList)
  2141. return;
  2142. let length = this.filterCallList.slice(0, 10).length;
  2143. if (e.keyCode === 13) {
  2144. this.setCall(this.filterCallList[this.selectCallIndex]);
  2145. e.preventDefault();
  2146. }
  2147. if (e.keyCode === 38) {
  2148. this.selectCallIndex--;
  2149. if (this.selectCallIndex < 0) {
  2150. this.selectCallIndex = length - 1;
  2151. }
  2152. e.preventDefault();
  2153. }
  2154. if (e.keyCode === 40) {
  2155. this.selectCallIndex++;
  2156. if (this.selectCallIndex > length - 1) {
  2157. this.selectCallIndex = 0;
  2158. }
  2159. e.preventDefault();
  2160. }
  2161. },
  2162. changeOption(item) {
  2163. this.$emit("update:displayType", item);
  2164. },
  2165. addThank() {
  2166. eventBus.emit(CMD$1.CHANGE_POST_THANK, { id: this.post.id, type: "add" });
  2167. },
  2168. recallThank() {
  2169. eventBus.emit(CMD$1.CHANGE_POST_THANK, { id: this.post.id, type: "recall" });
  2170. },
  2171. scrollTop() {
  2172. if (this.isPost) {
  2173. $("body , html").animate({ scrollTop: 0 }, 300);
  2174. } else {
  2175. this.$refs.detail.scrollTo({ top: 0, behavior: "smooth" });
  2176. }
  2177. }
  2178. }
  2179. };
  2180. const _withScopeId$3 = (n) => (vue.pushScopeId("data-v-13ba59da"), n = n(), vue.popScopeId(), n);
  2181. const _hoisted_1$6 = { class: "my-box post-wrapper" };
  2182. const _hoisted_2$5 = { class: "toolbar-wrapper" };
  2183. const _hoisted_3$4 = {
  2184. key: 0,
  2185. class: "my-box"
  2186. };
  2187. const _hoisted_4$1 = { class: "my-cell flex" };
  2188. const _hoisted_5$1 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("span", { class: "gray" }, "高赞回复", -1));
  2189. const _hoisted_6$1 = { class: "top-reply" };
  2190. const _hoisted_7 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("i", { class: "fa fa-times" }, null, -1));
  2191. const _hoisted_8 = { ref: "topReply" };
  2192. const _hoisted_9 = { class: "my-box comment-wrapper" };
  2193. const _hoisted_10 = {
  2194. key: 0,
  2195. class: "my-cell flex"
  2196. };
  2197. const _hoisted_11 = {
  2198. key: 0,
  2199. class: "read-notice"
  2200. };
  2201. const _hoisted_12 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("span", null, "上次打开:", -1));
  2202. const _hoisted_13 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("i", { class: "fa fa-long-arrow-down" }, null, -1));
  2203. const _hoisted_14 = [
  2204. _hoisted_13
  2205. ];
  2206. const _hoisted_15 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("i", { class: "fa fa-long-arrow-down" }, null, -1));
  2207. const _hoisted_16 = [
  2208. _hoisted_15
  2209. ];
  2210. const _hoisted_17 = { class: "my-cell flex" };
  2211. const _hoisted_18 = { class: "gray" };
  2212. const _hoisted_19 = { key: 0 };
  2213. const _hoisted_20 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("strong", { class: "snow" }, "•", -1));
  2214. const _hoisted_21 = ["innerHTML"];
  2215. const _hoisted_22 = {
  2216. key: 0,
  2217. class: "loading-wrapper"
  2218. };
  2219. const _hoisted_23 = {
  2220. key: 1,
  2221. class: "comments",
  2222. ref: "comments"
  2223. };
  2224. const _hoisted_24 = {
  2225. key: 1,
  2226. id: "no-comments-yet"
  2227. };
  2228. const _hoisted_25 = { class: "my-cell flex" };
  2229. const _hoisted_26 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("span", null, "添加一条新回复", -1));
  2230. const _hoisted_27 = { class: "notice-right" };
  2231. const _hoisted_28 = { class: "w" };
  2232. const _hoisted_29 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("span", { class: "gray" }, "上下文", -1));
  2233. const _hoisted_30 = { class: "top-reply" };
  2234. const _hoisted_31 = ["onClick"];
  2235. const _hoisted_32 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("i", {
  2236. class: "fa fa-times",
  2237. "aria-hidden": "true"
  2238. }, null, -1));
  2239. const _hoisted_33 = [
  2240. _hoisted_32
  2241. ];
  2242. const _hoisted_34 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("i", {
  2243. class: "fa fa-long-arrow-up",
  2244. "aria-hidden": "true"
  2245. }, null, -1));
  2246. const _hoisted_35 = [
  2247. _hoisted_34
  2248. ];
  2249. const _hoisted_36 = /* @__PURE__ */ _withScopeId$3(() => /* @__PURE__ */ vue.createElementVNode("i", { class: "fa fa-long-arrow-down" }, null, -1));
  2250. function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
  2251. const _component_BaseHtmlRender = vue.resolveComponent("BaseHtmlRender");
  2252. const _component_Point = vue.resolveComponent("Point");
  2253. const _component_Toolbar = vue.resolveComponent("Toolbar");
  2254. const _component_Tooltip = vue.resolveComponent("Tooltip");
  2255. const _component_PopConfirm = vue.resolveComponent("PopConfirm");
  2256. const _component_Comment = vue.resolveComponent("Comment");
  2257. const _component_PostEditor = vue.resolveComponent("PostEditor");
  2258. const _component_SingleComment = vue.resolveComponent("SingleComment");
  2259. return vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", {
  2260. class: vue.normalizeClass(["post-detail", [$options.isNight ? "isNight" : "", $options.pageType]]),
  2261. ref: "detail",
  2262. onKeydown: _cache[23] || (_cache[23] = vue.withKeys(($event) => $options.close(), ["esc"])),
  2263. onScroll: _cache[24] || (_cache[24] = (...args) => $data.debounceScroll && $data.debounceScroll(...args)),
  2264. onClick: _cache[25] || (_cache[25] = ($event) => $options.close("space"))
  2265. }, [
  2266. vue.createElementVNode("div", {
  2267. ref: "main",
  2268. class: "main",
  2269. tabindex: "1",
  2270. onClick: _cache[22] || (_cache[22] = vue.withModifiers((...args) => $options.stop && $options.stop(...args), ["stop"]))
  2271. }, [
  2272. vue.createElementVNode("div", {
  2273. class: "main-wrapper",
  2274. style: vue.normalizeStyle({ width: $options.config.postWidth })
  2275. }, [
  2276. vue.createElementVNode("div", _hoisted_1$6, [
  2277. vue.createVNode(_component_BaseHtmlRender, {
  2278. html: $options.post.headerTemplate
  2279. }, null, 8, ["html"]),
  2280. vue.createElementVNode("div", _hoisted_2$5, [
  2281. vue.createVNode(_component_Point, {
  2282. onAddThank: $options.addThank,
  2283. onRecallThank: $options.recallThank,
  2284. full: false,
  2285. item: {
  2286. isThanked: $options.post.isThanked,
  2287. thankCount: $options.post.thankCount,
  2288. username: $options.post.username
  2289. },
  2290. "api-url": "topic/" + $options.post.id
  2291. }, null, 8, ["onAddThank", "onRecallThank", "item", "api-url"]),
  2292. vue.createVNode(_component_Toolbar, {
  2293. onReply: _cache[0] || (_cache[0] = ($event) => $data.isSticky = !$data.isSticky)
  2294. })
  2295. ])
  2296. ]),
  2297. $options.topReply.length && $options.config.showTopReply ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$4, [
  2298. vue.createElementVNode("div", _hoisted_4$1, [
  2299. _hoisted_5$1,
  2300. vue.createElementVNode("div", _hoisted_6$1, [
  2301. vue.createVNode(_component_Tooltip, {
  2302. title: `统计点赞数大于等于${$options.config.topReplyLoveMinCount}个的回复,可在设置中调整`
  2303. }, {
  2304. default: vue.withCtx(() => [
  2305. vue.createElementVNode("i", {
  2306. class: "fa fa-info",
  2307. onClick: _cache[1] || (_cache[1] = ($event) => $options.showConfig())
  2308. })
  2309. ]),
  2310. _: 1
  2311. }, 8, ["title"]),
  2312. vue.createVNode(_component_PopConfirm, {
  2313. title: "关闭后不再默认显示,可在设置里重新打开,确认关闭?",
  2314. onConfirm: _cache[2] || (_cache[2] = ($event) => $options.config.showTopReply = false)
  2315. }, {
  2316. default: vue.withCtx(() => [
  2317. _hoisted_7
  2318. ]),
  2319. _: 1
  2320. }),
  2321. vue.createVNode(_component_Tooltip, { title: "收起高赞回复" }, {
  2322. default: vue.withCtx(() => [
  2323. vue.createElementVNode("i", {
  2324. class: "fa fa-compress",
  2325. onClick: _cache[3] || (_cache[3] = (...args) => $options.collapseTopReplyList && $options.collapseTopReplyList(...args))
  2326. })
  2327. ]),
  2328. _: 1
  2329. })
  2330. ])
  2331. ]),
  2332. vue.createElementVNode("div", _hoisted_8, [
  2333. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($options.topReply, (item, index2) => {
  2334. return vue.openBlock(), vue.createBlock(_component_Comment, {
  2335. key: item.floor,
  2336. type: "top",
  2337. modelValue: $options.topReply[index2],
  2338. "onUpdate:modelValue": ($event) => $options.topReply[index2] = $event
  2339. }, null, 8, ["modelValue", "onUpdate:modelValue"]);
  2340. }), 128))
  2341. ], 512)
  2342. ])) : vue.createCommentVNode("", true),
  2343. vue.createElementVNode("div", _hoisted_9, [
  2344. $options.post.replyList.length || $props.loading ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
  2345. $options.config.showToolbar ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_10, [
  2346. vue.createElementVNode("div", {
  2347. class: vue.normalizeClass(["radio-group2", { isNight: $options.isNight }])
  2348. }, [
  2349. vue.createElementVNode("div", {
  2350. class: vue.normalizeClass(["radio", $props.displayType === 0 ? "active" : ""]),
  2351. onClick: _cache[4] || (_cache[4] = ($event) => $options.changeOption(0))
  2352. }, "楼中楼 ", 2),
  2353. vue.createElementVNode("div", {
  2354. class: vue.normalizeClass(["radio", $props.displayType === 1 ? "active" : ""]),
  2355. onClick: _cache[5] || (_cache[5] = ($event) => $options.changeOption(1))
  2356. }, "感谢 ", 2),
  2357. vue.createElementVNode("div", {
  2358. class: vue.normalizeClass(["radio", $props.displayType === 3 ? "active" : ""]),
  2359. onClick: _cache[6] || (_cache[6] = ($event) => $options.changeOption(3))
  2360. }, "只看楼主 ", 2),
  2361. vue.createElementVNode("div", {
  2362. class: vue.normalizeClass(["radio", $props.displayType === 2 ? "active" : ""]),
  2363. onClick: _cache[7] || (_cache[7] = ($event) => $options.changeOption(2))
  2364. }, "V2原版 ", 2)
  2365. ], 2),
  2366. $data.read.floor || $data.read.total ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11, [
  2367. _hoisted_12,
  2368. $data.read.floor ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
  2369. vue.createElementVNode("span", null, [
  2370. vue.createTextVNode("阅读到"),
  2371. vue.createElementVNode("b", null, vue.toDisplayString($data.read.floor), 1),
  2372. vue.createTextVNode("楼")
  2373. ]),
  2374. vue.createElementVNode("div", {
  2375. class: "jump jump1",
  2376. onClick: _cache[8] || (_cache[8] = ($event) => $options.jump($data.read.floor))
  2377. }, _hoisted_14)
  2378. ], 64)) : vue.createCommentVNode("", true),
  2379. vue.createElementVNode("span", null, [
  2380. vue.createTextVNode("总楼层"),
  2381. vue.createElementVNode("b", null, vue.toDisplayString($data.read.total), 1)
  2382. ]),
  2383. vue.createElementVNode("div", {
  2384. class: "jump",
  2385. onClick: _cache[9] || (_cache[9] = ($event) => $options.jump($data.read.total))
  2386. }, _hoisted_16)
  2387. ])) : vue.createCommentVNode("", true)
  2388. ])) : vue.createCommentVNode("", true),
  2389. vue.createElementVNode("div", _hoisted_17, [
  2390. vue.createElementVNode("span", _hoisted_18, [
  2391. vue.createTextVNode(vue.toDisplayString($options.post.replyCount) + " 条回复 ", 1),
  2392. $options.post.createDate ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_19, [
  2393. vue.createTextVNode("  "),
  2394. _hoisted_20,
  2395. vue.createTextVNode("  " + vue.toDisplayString($options.post.createDate), 1)
  2396. ])) : vue.createCommentVNode("", true)
  2397. ]),
  2398. vue.createElementVNode("div", {
  2399. class: "fr",
  2400. innerHTML: $options.post.fr
  2401. }, null, 8, _hoisted_21)
  2402. ])
  2403. ], 64)) : vue.createCommentVNode("", true),
  2404. $options.replyList.length || $props.loading ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [
  2405. $props.loading ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_22, [
  2406. vue.createElementVNode("div", {
  2407. class: vue.normalizeClass([$options.isNight ? "loading-b" : "loading-c"])
  2408. }, null, 2)
  2409. ])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_23, [
  2410. $props.modelValue ? (vue.openBlock(true), vue.createElementBlock(vue.Fragment, { key: 0 }, vue.renderList($options.replyList, (item, index2) => {
  2411. return vue.openBlock(), vue.createBlock(_component_Comment, {
  2412. key: item.floor,
  2413. modelValue: $options.replyList[index2],
  2414. "onUpdate:modelValue": ($event) => $options.replyList[index2] = $event
  2415. }, null, 8, ["modelValue", "onUpdate:modelValue"]);
  2416. }), 128)) : vue.createCommentVNode("", true)
  2417. ], 512))
  2418. ], 64)) : vue.createCommentVNode("", true)
  2419. ]),
  2420. !($options.replyList.length || $props.loading) ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_24, "目前尚无回复")) : vue.createCommentVNode("", true),
  2421. $options.isLogin ? (vue.openBlock(), vue.createElementBlock("div", {
  2422. key: 2,
  2423. class: vue.normalizeClass(["my-box editor-wrapper", { "sticky": $data.isSticky }]),
  2424. ref: "replyBox"
  2425. }, [
  2426. vue.createElementVNode("div", _hoisted_25, [
  2427. _hoisted_26,
  2428. vue.createElementVNode("div", _hoisted_27, [
  2429. $data.isSticky ? (vue.openBlock(), vue.createElementBlock("a", {
  2430. key: 0,
  2431. class: "float",
  2432. onClick: _cache[10] || (_cache[10] = ($event) => $data.isSticky = false)
  2433. }, "取消回复框停靠")) : vue.createCommentVNode("", true),
  2434. vue.createElementVNode("a", {
  2435. onClick: _cache[11] || (_cache[11] = (...args) => $options.scrollTop && $options.scrollTop(...args))
  2436. }, "回到顶部")
  2437. ])
  2438. ]),
  2439. vue.createElementVNode("div", _hoisted_28, [
  2440. vue.createVNode(_component_PostEditor, {
  2441. onClose: $options.goBottom,
  2442. ref: "post-editor",
  2443. useType: "reply-post",
  2444. onClick: _cache[12] || (_cache[12] = ($event) => $data.isSticky = true)
  2445. }, null, 8, ["onClose"])
  2446. ])
  2447. ], 2)) : vue.createCommentVNode("", true)
  2448. ], 4),
  2449. $data.showRelationReply ? (vue.openBlock(), vue.createElementBlock("div", {
  2450. key: 0,
  2451. class: "relationReply",
  2452. onClick: _cache[16] || (_cache[16] = ($event) => $options.close("space"))
  2453. }, [
  2454. vue.createElementVNode("div", {
  2455. class: "my-cell flex",
  2456. onClick: _cache[14] || (_cache[14] = vue.withModifiers((...args) => $options.stop && $options.stop(...args), ["stop"]))
  2457. }, [
  2458. _hoisted_29,
  2459. vue.createElementVNode("div", _hoisted_30, [
  2460. vue.createElementVNode("i", {
  2461. class: "fa fa-times",
  2462. onClick: _cache[13] || (_cache[13] = ($event) => $data.showRelationReply = false)
  2463. })
  2464. ])
  2465. ]),
  2466. vue.createElementVNode("div", {
  2467. class: "comments",
  2468. onClick: _cache[15] || (_cache[15] = vue.withModifiers((...args) => $options.stop && $options.stop(...args), ["stop"]))
  2469. }, [
  2470. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($options.relationReply, (item, index2) => {
  2471. return vue.openBlock(), vue.createBlock(_component_SingleComment, {
  2472. "is-right": item.username === $data.targetUser.right,
  2473. key: item.floor,
  2474. comment: item
  2475. }, null, 8, ["is-right", "comment"]);
  2476. }), 128))
  2477. ])
  2478. ])) : vue.createCommentVNode("", true),
  2479. $data.showCallList && $options.filterCallList.length ? (vue.openBlock(), vue.createElementBlock("div", {
  2480. key: 1,
  2481. class: "call-list",
  2482. style: vue.normalizeStyle($data.callStyle)
  2483. }, [
  2484. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($options.filterCallList.slice(0, 10), (item, index2) => {
  2485. return vue.openBlock(), vue.createElementBlock("div", {
  2486. class: vue.normalizeClass(["call-item", { select: index2 === $data.selectCallIndex }]),
  2487. onClick: ($event) => $options.setCall(item)
  2488. }, [
  2489. vue.createElementVNode("a", null, vue.toDisplayString(item), 1)
  2490. ], 10, _hoisted_31);
  2491. }), 256))
  2492. ], 4)) : vue.createCommentVNode("", true),
  2493. $options.config.closePostDetailBySpace ? (vue.openBlock(), vue.createElementBlock("div", {
  2494. key: 2,
  2495. class: "close-btn",
  2496. onClick: _cache[17] || (_cache[17] = ($event) => $options.close("btn"))
  2497. }, _hoisted_33)) : vue.createCommentVNode("", true),
  2498. vue.createElementVNode("div", {
  2499. class: "scroll-top button",
  2500. onClick: _cache[18] || (_cache[18] = vue.withModifiers((...args) => $options.scrollTop && $options.scrollTop(...args), ["stop"]))
  2501. }, _hoisted_35),
  2502. vue.createElementVNode("div", {
  2503. class: "scroll-to button",
  2504. onClick: _cache[21] || (_cache[21] = vue.withModifiers(($event) => $options.jump($data.currentFloor), ["stop"]))
  2505. }, [
  2506. _hoisted_36,
  2507. vue.withDirectives(vue.createElementVNode("input", {
  2508. type: "text",
  2509. "onUpdate:modelValue": _cache[19] || (_cache[19] = ($event) => $data.currentFloor = $event),
  2510. onClick: _cache[20] || (_cache[20] = vue.withModifiers((...args) => $options.stop && $options.stop(...args), ["stop"]))
  2511. }, null, 512), [
  2512. [vue.vModelText, $data.currentFloor]
  2513. ])
  2514. ])
  2515. ], 512)
  2516. ], 34)), [
  2517. [vue.vShow, $props.modelValue]
  2518. ]);
  2519. }
  2520. const PostDetail = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["render", _sfc_render$3], ["__scopeId", "data-v-13ba59da"]]);
  2521. const _withScopeId$2 = (n) => (vue.pushScopeId("data-v-19fe372d"), n = n(), vue.popScopeId(), n);
  2522. const _hoisted_1$5 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ vue.createElementVNode("svg", {
  2523. width: "24",
  2524. height: "24",
  2525. viewBox: "0 0 48 48",
  2526. fill: "none",
  2527. xmlns: "http://www.w3.org/2000/svg"
  2528. }, [
  2529. /* @__PURE__ */ vue.createElementVNode("path", {
  2530. d: "M17 32L19.1875 27M31 32L28.8125 27M19.1875 27L24 16L28.8125 27M19.1875 27H28.8125",
  2531. stroke: "#929596",
  2532. "stroke-width": "4",
  2533. "stroke-linecap": "round",
  2534. "stroke-linejoin": "round"
  2535. }),
  2536. /* @__PURE__ */ vue.createElementVNode("path", {
  2537. d: "M43.1999 20C41.3468 10.871 33.2758 4 23.5999 4C13.9241 4 5.85308 10.871 4 20L10 18",
  2538. stroke: "#929596",
  2539. "stroke-width": "4",
  2540. "stroke-linecap": "round",
  2541. "stroke-linejoin": "round"
  2542. }),
  2543. /* @__PURE__ */ vue.createElementVNode("path", {
  2544. d: "M4 28C5.85308 37.129 13.9241 44 23.5999 44C33.2758 44 41.3468 37.129 43.1999 28L38 30",
  2545. stroke: "#929596",
  2546. "stroke-width": "4",
  2547. "stroke-linecap": "round",
  2548. "stroke-linejoin": "round"
  2549. })
  2550. ], -1));
  2551. const _hoisted_2$4 = { key: 1 };
  2552. const _sfc_main$5 = {
  2553. __name: "Base64Tooltip",
  2554. setup(__props) {
  2555. const tooltip = vue.ref(null);
  2556. const show = vue.ref(false);
  2557. const originalText = vue.ref("");
  2558. const decodeText = vue.ref("");
  2559. const styleObject = vue.reactive({
  2560. left: "-100vw",
  2561. top: "-100vh"
  2562. });
  2563. vue.onMounted(() => {
  2564. eventBus.on(CMD$1.SHOW_TOOLTIP, ({ text, e }) => {
  2565. setTimeout(() => show.value = true);
  2566. originalText.value = text;
  2567. decodeText.value = "";
  2568. styleObject.left = e.clientX + "px";
  2569. styleObject.top = e.clientY + 20 + "px";
  2570. });
  2571. window.addEventListener("click", (e) => {
  2572. if (!tooltip.value)
  2573. return;
  2574. if (!tooltip.value.contains(e.target) && show.value) {
  2575. show.value = false;
  2576. }
  2577. }, { capture: true });
  2578. const fn = () => show.value && (show.value = false);
  2579. $(".post-detail", window.win().doc).on("scroll", fn);
  2580. });
  2581. function copy() {
  2582. if (window.win().navigator.clipboard) {
  2583. window.win().navigator.clipboard.writeText(decodeText.value);
  2584. eventBus.emit(CMD$1.SHOW_MSG, { type: "success", text: "复制成功" });
  2585. } else {
  2586. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "复制失败!浏览器不支持!" });
  2587. }
  2588. }
  2589. function base64ToArrayBuffer(base64) {
  2590. let binary_string = window.atob(base64);
  2591. let len = binary_string.length;
  2592. let bytes = new Uint8Array(len);
  2593. for (let i = 0; i < len; i++) {
  2594. bytes[i] = binary_string.charCodeAt(i);
  2595. }
  2596. return bytes.buffer;
  2597. }
  2598. function decode() {
  2599. try {
  2600. new Blob([base64ToArrayBuffer(originalText.value)]).text().then((r) => {
  2601. decodeText.value = r;
  2602. });
  2603. } catch (e) {
  2604. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "Base64解码失败!不是标准数据!" });
  2605. }
  2606. }
  2607. return (_ctx, _cache) => {
  2608. return vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", {
  2609. class: "base64_tooltip",
  2610. style: vue.normalizeStyle(styleObject),
  2611. onClick: decode,
  2612. ref_key: "tooltip",
  2613. ref: tooltip
  2614. }, [
  2615. !decodeText.value ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
  2616. vue.createTextVNode(" Base64解码:" + vue.toDisplayString(originalText.value) + " ", 1),
  2617. _hoisted_1$5
  2618. ], 64)) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$4, [
  2619. vue.createElementVNode("span", null, vue.toDisplayString(decodeText.value), 1),
  2620. vue.createElementVNode("div", {
  2621. class: "button",
  2622. onClick: copy
  2623. }, "点击复制")
  2624. ]))
  2625. ], 4)), [
  2626. [vue.vShow, show.value]
  2627. ]);
  2628. };
  2629. }
  2630. };
  2631. const Base64Tooltip = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-19fe372d"]]);
  2632. const _sfc_main$4 = {
  2633. name: "Msg",
  2634. props: {
  2635. type: "",
  2636. text: ""
  2637. },
  2638. created() {
  2639. setTimeout(() => {
  2640. this.$emit("close");
  2641. }, 3e3);
  2642. }
  2643. };
  2644. const _withScopeId$1 = (n) => (vue.pushScopeId("data-v-4dd6db95"), n = n(), vue.popScopeId(), n);
  2645. const _hoisted_1$4 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("svg", {
  2646. width: "24",
  2647. height: "24",
  2648. viewBox: "0 0 48 48",
  2649. fill: "none",
  2650. xmlns: "http://www.w3.org/2000/svg"
  2651. }, [
  2652. /* @__PURE__ */ vue.createElementVNode("path", {
  2653. d: "M14 14L34 34",
  2654. stroke: "#ffffff",
  2655. "stroke-width": "4",
  2656. "stroke-linecap": "round",
  2657. "stroke-linejoin": "round"
  2658. }),
  2659. /* @__PURE__ */ vue.createElementVNode("path", {
  2660. d: "M14 34L34 14",
  2661. stroke: "#ffffff",
  2662. "stroke-width": "4",
  2663. "stroke-linecap": "round",
  2664. "stroke-linejoin": "round"
  2665. })
  2666. ], -1));
  2667. const _hoisted_2$3 = [
  2668. _hoisted_1$4
  2669. ];
  2670. const _hoisted_3$3 = { class: "right" };
  2671. function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
  2672. return vue.openBlock(), vue.createElementBlock("div", {
  2673. class: vue.normalizeClass(["msg", $props.type])
  2674. }, [
  2675. vue.createElementVNode("div", {
  2676. class: "left",
  2677. onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("close"))
  2678. }, _hoisted_2$3),
  2679. vue.createElementVNode("div", _hoisted_3$3, vue.toDisplayString($props.text), 1)
  2680. ], 2);
  2681. }
  2682. const Msg$1 = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$2], ["__scopeId", "data-v-4dd6db95"]]);
  2683. const CMD = {
  2684. SHOW_TOOLTIP: "SHOW_TOOLTIP",
  2685. SHOW_MSG: "SHOW_MSG",
  2686. SET_CALL: "SET_CALL",
  2687. SHOW_CALL: "SHOW_CALL",
  2688. REFRESH_ONCE: "REFRESH_ONCE",
  2689. ADD_REPLY: "ADD_REPLY",
  2690. IGNORE: "IGNORE",
  2691. MERGE: "MERGE",
  2692. REMOVE: "REMOVE",
  2693. CHANGE_COMMENT_THANK: "CHANGE_COMMENT_THANK",
  2694. CHANGE_POST_THANK: "CHANGE_POST_THANK",
  2695. ADD_TAG: "ADD_TAG",
  2696. REMOVE_TAG: "REMOVE_TAG"
  2697. };
  2698. const _withScopeId = (n) => (vue.pushScopeId("data-v-66e306a2"), n = n(), vue.popScopeId(), n);
  2699. const _hoisted_1$3 = { class: "wrapper" };
  2700. const _hoisted_2$2 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "title" }, " 添加标签 ", -1));
  2701. const _hoisted_3$2 = { class: "option" };
  2702. const _hoisted_4 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", null, "用户:", -1));
  2703. const _hoisted_5 = ["onKeydown"];
  2704. const _hoisted_6 = { class: "btns" };
  2705. const _sfc_main$3 = {
  2706. __name: "TagModal",
  2707. props: ["tags"],
  2708. emits: ["update:tags"],
  2709. setup(__props, { emit }) {
  2710. const props = __props;
  2711. const tagModal = vue.reactive({
  2712. show: false,
  2713. currentUsername: "",
  2714. tag: ""
  2715. });
  2716. const isNight = vue.inject("isNight");
  2717. const input = vue.ref < HTMLInputElement > null;
  2718. vue.onMounted(() => {
  2719. eventBus.on(CMD.ADD_TAG, (username) => {
  2720. tagModal.currentUsername = username;
  2721. tagModal.show = true;
  2722. vue.nextTick(() => {
  2723. input.value.focus();
  2724. });
  2725. });
  2726. });
  2727. async function addTag() {
  2728. let oldTag = window.clone(props.tags);
  2729. let tempTag = window.clone(props.tags);
  2730. let userTags = tempTag[tagModal.currentUsername] ?? [];
  2731. let rIndex = userTags.findIndex((v) => v === tagModal.tag);
  2732. if (rIndex > -1) {
  2733. eventBus.emit(CMD.SHOW_MSG, { type: "warning", text: "标签已存在!" });
  2734. return;
  2735. } else {
  2736. userTags.push(tagModal.tag);
  2737. }
  2738. tempTag[tagModal.currentUsername] = userTags;
  2739. emit("update:tags", tempTag);
  2740. tagModal.tag = "";
  2741. tagModal.show = false;
  2742. let res = await window.parse.saveTags(tempTag);
  2743. if (!res) {
  2744. eventBus.emit(CMD.SHOW_MSG, { type: "error", text: "标签添加失败!" });
  2745. emit("update:tags", oldTag);
  2746. }
  2747. }
  2748. return (_ctx, _cache) => {
  2749. return vue.openBlock(), vue.createBlock(vue.Transition, null, {
  2750. default: vue.withCtx(() => [
  2751. tagModal.show ? (vue.openBlock(), vue.createElementBlock("div", {
  2752. key: 0,
  2753. class: vue.normalizeClass(["tag-modal modal", { isNight: vue.unref(isNight) }])
  2754. }, [
  2755. vue.createElementVNode("div", {
  2756. class: "mask",
  2757. onClick: _cache[0] || (_cache[0] = vue.withModifiers(($event) => tagModal.show = false, ["stop"]))
  2758. }),
  2759. vue.createElementVNode("div", _hoisted_1$3, [
  2760. _hoisted_2$2,
  2761. vue.createElementVNode("div", _hoisted_3$2, [
  2762. _hoisted_4,
  2763. vue.createElementVNode("div", null, vue.toDisplayString(tagModal.currentUsername), 1)
  2764. ]),
  2765. vue.withDirectives(vue.createElementVNode("input", {
  2766. type: "text",
  2767. ref: "input",
  2768. style: { "width": "100%" },
  2769. autofocus: "",
  2770. "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => tagModal.tag = $event),
  2771. onKeydown: vue.withKeys(addTag, ["enter"])
  2772. }, null, 40, _hoisted_5), [
  2773. [vue.vModelText, tagModal.tag]
  2774. ]),
  2775. vue.createElementVNode("div", _hoisted_6, [
  2776. vue.createElementVNode("div", {
  2777. class: "white",
  2778. onClick: _cache[2] || (_cache[2] = ($event) => tagModal.show = false)
  2779. }, "取消"),
  2780. vue.createElementVNode("div", {
  2781. class: "main",
  2782. onClick: addTag
  2783. }, "确定")
  2784. ])
  2785. ])
  2786. ], 2)) : vue.createCommentVNode("", true)
  2787. ]),
  2788. _: 1
  2789. });
  2790. };
  2791. }
  2792. };
  2793. const TagModal = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-66e306a2"]]);
  2794. const _sfc_main$2 = {
  2795. name: "Msg",
  2796. props: {
  2797. type: "",
  2798. text: ""
  2799. },
  2800. created() {
  2801. setTimeout(() => {
  2802. this.$emit("close");
  2803. }, 3e3);
  2804. }
  2805. };
  2806. const _hoisted_1$2 = /* @__PURE__ */ vue.createElementVNode("svg", {
  2807. width: "24",
  2808. height: "24",
  2809. viewBox: "0 0 48 48",
  2810. fill: "none",
  2811. xmlns: "http://www.w3.org/2000/svg"
  2812. }, [
  2813. /* @__PURE__ */ vue.createElementVNode("path", {
  2814. d: "M14 14L34 34",
  2815. stroke: "#ffffff",
  2816. "stroke-width": "4",
  2817. "stroke-linecap": "round",
  2818. "stroke-linejoin": "round"
  2819. }),
  2820. /* @__PURE__ */ vue.createElementVNode("path", {
  2821. d: "M14 34L34 14",
  2822. stroke: "#ffffff",
  2823. "stroke-width": "4",
  2824. "stroke-linecap": "round",
  2825. "stroke-linejoin": "round"
  2826. })
  2827. ], -1);
  2828. const _hoisted_2$1 = [
  2829. _hoisted_1$2
  2830. ];
  2831. const _hoisted_3$1 = { class: "right" };
  2832. function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
  2833. return vue.openBlock(), vue.createElementBlock("div", {
  2834. class: vue.normalizeClass(["msg", $props.type])
  2835. }, [
  2836. vue.createElementVNode("div", {
  2837. class: "left",
  2838. onClick: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("close"))
  2839. }, _hoisted_2$1),
  2840. vue.createElementVNode("div", _hoisted_3$1, vue.toDisplayString($props.text), 1)
  2841. ], 2);
  2842. }
  2843. const Msg = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1]]);
  2844. const _hoisted_1$1 = { class: "msgs" };
  2845. const _sfc_main$1 = {
  2846. __name: "MsgModal",
  2847. setup(__props) {
  2848. const msgList = vue.reactive([
  2849. // {type: 'success', text: '123', id: Date.now()}
  2850. ]);
  2851. vue.onMounted(() => {
  2852. eventBus.on(CMD$1.SHOW_MSG, (val) => {
  2853. msgList.push({ ...val, id: Date.now() });
  2854. });
  2855. });
  2856. function removeMsg(id) {
  2857. let rIndex = msgList.findIndex((item) => item.id === id);
  2858. if (rIndex > -1) {
  2859. msgList.splice(rIndex, 1);
  2860. }
  2861. }
  2862. return (_ctx, _cache) => {
  2863. return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$1, [
  2864. (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(msgList, (v) => {
  2865. return vue.openBlock(), vue.createBlock(Msg, {
  2866. key: v.id,
  2867. type: v.type,
  2868. text: v.text,
  2869. onClose: ($event) => removeMsg(v.id)
  2870. }, null, 8, ["type", "text", "onClose"]);
  2871. }), 128))
  2872. ]);
  2873. };
  2874. }
  2875. };
  2876. const MsgModal = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-95974c3e"]]);
  2877. const _sfc_main = {
  2878. components: { MsgModal, TagModal, Tooltip, Setting, PostDetail, Base64Tooltip, Msg: Msg$1 },
  2879. provide() {
  2880. return {
  2881. isLogin: vue.computed(() => this.isLogin),
  2882. isNight: vue.computed(() => this.isNight),
  2883. pageType: vue.computed(() => this.pageType),
  2884. tags: vue.computed(() => this.tags),
  2885. show: vue.computed(() => this.show),
  2886. post: vue.computed(() => this.current),
  2887. config: vue.computed(() => this.config),
  2888. allReplyUsers: vue.computed(() => {
  2889. var _a, _b, _c;
  2890. if ((_a = this.current) == null ? void 0 : _a.replyList) {
  2891. return Array.from(new Set(((_c = (_b = this.current) == null ? void 0 : _b.replyList) == null ? void 0 : _c.map((v) => v.username)) ?? []));
  2892. }
  2893. return [];
  2894. }),
  2895. showConfig: this.showConfig
  2896. };
  2897. },
  2898. data() {
  2899. return {
  2900. loading: window.pageType === PageType.Post,
  2901. loadMore: false,
  2902. isLogin: !!window.user.username,
  2903. pageType: window.pageType,
  2904. isNight: window.isNight,
  2905. stopMe: false,
  2906. //停止使用脚本
  2907. show: false,
  2908. current: window.clone(window.initPost),
  2909. list: [],
  2910. config: window.clone(window.config),
  2911. tags: window.user.tags,
  2912. readList: window.user.readList,
  2913. configModal: {
  2914. show: false
  2915. },
  2916. tagModal: {
  2917. show: false,
  2918. currentUsername: "",
  2919. tag: ""
  2920. }
  2921. };
  2922. },
  2923. computed: {
  2924. isList() {
  2925. return this.pageType !== PageType.Post;
  2926. },
  2927. isPost() {
  2928. return this.pageType === PageType.Post;
  2929. }
  2930. },
  2931. watch: {
  2932. "current.replyList": {
  2933. handler(newVal, oldVal) {
  2934. if (newVal.length) {
  2935. this.current.replyCount = newVal.length;
  2936. let res = window.parse.createNestedList(newVal, this.current.allReplyUsers);
  2937. if (res) {
  2938. this.current.nestedReplies = res;
  2939. }
  2940. } else {
  2941. this.current.replyCount = 0;
  2942. this.current.nestedReplies = [];
  2943. }
  2944. if (this.list) {
  2945. let rIndex = this.list.findIndex((i) => i.id === this.current.id);
  2946. if (rIndex > -1) {
  2947. this.list[rIndex].replyCount = newVal.length;
  2948. }
  2949. }
  2950. },
  2951. deep: true
  2952. },
  2953. config: {
  2954. handler(newVal) {
  2955. let config = { [window.user.username ?? "default"]: newVal };
  2956. localStorage.setItem("v2ex-config", JSON.stringify(config));
  2957. window.config = newVal;
  2958. },
  2959. deep: true
  2960. },
  2961. tags(newVal) {
  2962. window.user.tags = newVal;
  2963. },
  2964. "config.viewType"(newVal) {
  2965. if (!newVal)
  2966. return;
  2967. if (newVal === "card") {
  2968. $(".post-item").each(function() {
  2969. $(this).addClass("preview");
  2970. });
  2971. } else {
  2972. $(".post-item").each(function() {
  2973. $(this).removeClass("preview");
  2974. });
  2975. }
  2976. }
  2977. },
  2978. created() {
  2979. window.cb = this.winCb;
  2980. if (window.win().canParseV2exPage) {
  2981. if (this.isList)
  2982. ;
  2983. }
  2984. $(document).on("click", "a", (e) => {
  2985. if (this.stopMe)
  2986. return true;
  2987. let { href, id, title } = window.parse.parseA(e.currentTarget);
  2988. if (this.clickPost(e, id, href, title)) {
  2989. return false;
  2990. }
  2991. });
  2992. let that = this;
  2993. $(document).on("click", ".post-item", function(e) {
  2994. if (this.classList.contains("preview")) {
  2995. if (e.target.tagName !== "A" && e.target.tagName !== "IMG" && !e.target.classList.contains("toggle")) {
  2996. console.log("点空白处");
  2997. let id = this.dataset["id"];
  2998. let href = this.dataset["href"];
  2999. if (that.clickPost(e, id, href)) {
  3000. return false;
  3001. } else {
  3002. location.href = href;
  3003. }
  3004. }
  3005. }
  3006. });
  3007. $(document).on("click", ".toggle", (e) => {
  3008. let id = e.currentTarget.dataset["id"];
  3009. let itemDom = window.win().query(`.id_${id}`);
  3010. if (itemDom.classList.contains("preview")) {
  3011. itemDom.classList.remove("preview");
  3012. } else {
  3013. itemDom.classList.add("preview");
  3014. }
  3015. });
  3016. window.onpopstate = (event) => {
  3017. if (event.state) {
  3018. if (!this.show)
  3019. this.show = true;
  3020. } else {
  3021. if (this.show)
  3022. this.show = false;
  3023. }
  3024. };
  3025. window.onbeforeunload = () => {
  3026. this.saveReadList();
  3027. };
  3028. this.initEvent();
  3029. },
  3030. beforeUnmount() {
  3031. eventBus.clear();
  3032. },
  3033. methods: {
  3034. saveReadList() {
  3035. window.parse.saveReadList(this.readList);
  3036. },
  3037. clickPost(e, id, href, title = "") {
  3038. var _a, _b, _c, _d, _e, _f;
  3039. if (id) {
  3040. if (this.config.clickPostItemOpenDetail) {
  3041. let index2 = this.list.findIndex((v) => v.id == id);
  3042. let postItem = this.clone(window.initPost);
  3043. if (index2 > -1) {
  3044. postItem = this.list[index2];
  3045. } else {
  3046. postItem.title = title ?? "加载中";
  3047. }
  3048. postItem.id = id;
  3049. postItem.href = href;
  3050. if (!postItem.headerTemplate) {
  3051. let template = `
  3052. <div class="header">
  3053. <div class="fr">
  3054. <a href="/member/${((_a = postItem == null ? void 0 : postItem.member) == null ? void 0 : _a.username) ?? ""}">
  3055. <img src="${((_b = postItem == null ? void 0 : postItem.member) == null ? void 0 : _b.avatar_large) ?? ""}" class="avatar"
  3056. border="0"
  3057. align="default" width="73" style="width: 73px; max-height: 73px;" alt="${((_c = postItem == null ? void 0 : postItem.member) == null ? void 0 : _c.username) ?? ""}">
  3058. </a>
  3059. </div>
  3060. <a href="/">V2EX</a> <span class="chevron">&nbsp;›&nbsp;</span> <a href="${((_d = postItem == null ? void 0 : postItem.node) == null ? void 0 : _d.url) ?? ""}">${((_e = postItem == null ? void 0 : postItem.node) == null ? void 0 : _e.title) ?? ""}</a>
  3061. <div class="sep10"></div>
  3062. <h1>${(postItem == null ? void 0 : postItem.title) || "加载中..."}</h1>
  3063. <div id="topic_930514_votes" class="votes">
  3064. <a href="javascript:" onclick="null" class="vote">
  3065. <li class="fa fa-chevron-up"></li>
  3066. &nbsp;
  3067. </a> &nbsp;
  3068. <a href="javascript:" onclick="null" class="vote">
  3069. <li class="fa fa-chevron-down"></li>
  3070. </a>
  3071. </div> &nbsp;
  3072. <small class="gray">
  3073. <a href="/member/zyronon">${((_f = postItem == null ? void 0 : postItem.member) == null ? void 0 : _f.username) ?? ""}</a> ·
  3074. <span title="2023-04-07 11:32:28 +08:00">1 天前</span> · 0 次点击
  3075. </small>
  3076. </div>
  3077. <div class="cell">
  3078. <div class="topic_content">
  3079. <div class="markdown_body">
  3080. ${(postItem == null ? void 0 : postItem.content_rendered) ?? ""}
  3081. </div>
  3082. </div>
  3083. </div>
  3084. `;
  3085. postItem.headerTemplate = template;
  3086. }
  3087. this.getPostDetail(postItem);
  3088. e.preventDefault();
  3089. return true;
  3090. }
  3091. if (this.config.newTabOpen) {
  3092. let tempId = "a_blank_" + Date.now();
  3093. let a = win().doc.createElement("a");
  3094. a.setAttribute("href", href);
  3095. a.setAttribute("target", "_blank");
  3096. a.setAttribute("id", tempId);
  3097. if (!win().doc.getElementById(tempId)) {
  3098. win().doc.body.appendChild(a);
  3099. }
  3100. a.click();
  3101. return true;
  3102. }
  3103. }
  3104. },
  3105. showPost() {
  3106. this.show = true;
  3107. $(`#Wrapper #Main .box:lt(3)`).each(function() {
  3108. $(this).hide();
  3109. });
  3110. },
  3111. showConfig() {
  3112. this.configModal.show = true;
  3113. },
  3114. async winCb({ type, value }) {
  3115. if (type === "openSetting") {
  3116. this.configModal.show = true;
  3117. }
  3118. if (type === "restorePost") {
  3119. if (this.stopMe)
  3120. return;
  3121. this.stopMe = true;
  3122. this.show = false;
  3123. this.loading = false;
  3124. eventBus.emit(CMD$1.SHOW_MSG, { type: "warning", text: "脚本无法查看此页面!" });
  3125. $(`#Wrapper #Main .box:lt(3)`).each(function() {
  3126. $(this).show();
  3127. });
  3128. }
  3129. if (type === "postContent") {
  3130. if (this.stopMe)
  3131. return;
  3132. this.current = Object.assign(this.clone(this.current), this.clone(value));
  3133. if (this.config.autoOpenDetail) {
  3134. this.showPost();
  3135. }
  3136. }
  3137. if (type === "postReplies") {
  3138. if (this.stopMe)
  3139. return;
  3140. this.current = Object.assign(this.clone(this.current), this.clone(value));
  3141. console.log("当前帖子", this.current);
  3142. this.loading = false;
  3143. }
  3144. if (type === "syncData") {
  3145. this.list = window.postList;
  3146. this.config = window.config;
  3147. this.tags = window.user.tags;
  3148. this.readList = window.user.readList;
  3149. this.current.read = this.readList[this.current.id] ?? {};
  3150. if (this.show && this.isPost && this.current.read.floor) {
  3151. this.$refs.postDetail.read = this.current.read;
  3152. }
  3153. console.log("this.readList", this.readList);
  3154. }
  3155. },
  3156. clone(val) {
  3157. return window.clone(val);
  3158. },
  3159. initEvent() {
  3160. eventBus.on(CMD$1.CHANGE_COMMENT_THANK, (val) => {
  3161. console.log("CHANGE_COMMENT_THANK", val);
  3162. const { id, type } = val;
  3163. let currentI = this.current.replyList.findIndex((i) => i.id === id);
  3164. if (currentI > -1) {
  3165. this.current.replyList[currentI].isThanked = type === "add";
  3166. if (type === "add") {
  3167. this.current.replyList[currentI].thankCount++;
  3168. } else {
  3169. this.current.replyList[currentI].thankCount--;
  3170. }
  3171. }
  3172. });
  3173. eventBus.on(CMD$1.CHANGE_POST_THANK, (val) => {
  3174. const { id, type } = val;
  3175. this.current.isThanked = type === "add";
  3176. if (type === "add") {
  3177. this.current.thankCount++;
  3178. } else {
  3179. this.current.thankCount--;
  3180. }
  3181. let currentI = this.list.findIndex((i) => i.id === id);
  3182. if (currentI > -1) {
  3183. this.list[currentI].isThanked = type === "add";
  3184. if (type === "add") {
  3185. this.list[currentI].thankCount++;
  3186. } else {
  3187. this.list[currentI].thankCount++;
  3188. }
  3189. }
  3190. });
  3191. eventBus.on(CMD$1.REMOVE, (val) => {
  3192. let removeIndex = this.current.replyList.findIndex((i) => i.floor === val);
  3193. if (removeIndex > -1) {
  3194. this.current.replyList.splice(removeIndex, 1);
  3195. }
  3196. let rIndex = this.list.findIndex((i) => i.id === this.current.id);
  3197. if (rIndex > -1) {
  3198. this.list[rIndex] = Object.assign(this.list[rIndex], val);
  3199. }
  3200. });
  3201. eventBus.on(CMD$1.IGNORE, () => {
  3202. this.show = false;
  3203. let rIndex = this.list.findIndex((i) => i.id === this.current.id);
  3204. if (rIndex > -1) {
  3205. this.list.splice(rIndex, 1);
  3206. }
  3207. this.current = this.clone(window.initPost);
  3208. });
  3209. eventBus.on(CMD$1.MERGE, (val) => {
  3210. this.current = Object.assign(this.current, val);
  3211. let rIndex = this.list.findIndex((i) => i.id === this.current.id);
  3212. if (rIndex > -1) {
  3213. this.list[rIndex] = Object.assign(this.list[rIndex], val);
  3214. }
  3215. });
  3216. eventBus.on(CMD$1.ADD_READ, (val) => {
  3217. this.readList[this.current.id] = val;
  3218. });
  3219. eventBus.on(CMD$1.ADD_REPLY, (item) => {
  3220. this.current.replyList.push(item);
  3221. });
  3222. eventBus.on(CMD$1.REFRESH_ONCE, async (once) => {
  3223. if (once) {
  3224. if (typeof once === "string") {
  3225. let res = once.match(/var once = "([\d]+)";/);
  3226. if (res && res[1]) {
  3227. this.current.once = Number(res[1]);
  3228. return;
  3229. }
  3230. }
  3231. if (typeof once === "number") {
  3232. this.current.once = once;
  3233. return;
  3234. }
  3235. }
  3236. window.fetchOnce().then((r) => {
  3237. this.current.once = r;
  3238. });
  3239. });
  3240. eventBus.on(CMD$1.REMOVE_TAG, async ({ username, tag }) => {
  3241. let oldTag = this.clone(this.tags);
  3242. let tags = this.tags[username] ?? [];
  3243. let rIndex = tags.findIndex((v) => v === tag);
  3244. if (rIndex > -1) {
  3245. tags.splice(rIndex, 1);
  3246. }
  3247. this.tags[username] = tags;
  3248. let res = await window.parse.saveTags(this.tags);
  3249. if (!res) {
  3250. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "标签删除失败!" });
  3251. this.tags = oldTag;
  3252. }
  3253. });
  3254. },
  3255. async getPostDetail(post, event) {
  3256. this.current = Object.assign({}, window.initPost, post);
  3257. this.current.read = this.readList[this.current.id] ?? { floor: 0, total: 0 };
  3258. this.show = true;
  3259. let url = window.baseUrl + "/t/" + post.id;
  3260. document.body.style.overflow = "hidden";
  3261. window.history.pushState({}, 0, post.href ?? url);
  3262. let alreadyHasReply = this.current.replyList.length;
  3263. if (alreadyHasReply) {
  3264. this.$refs.postDetail.jumpLastRead(this.current.read.floor);
  3265. } else {
  3266. this.loading = true;
  3267. }
  3268. let apiRes = await window.fetch(url + "?p=1");
  3269. if (apiRes.status === 404) {
  3270. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "主题未找到" });
  3271. return this.loading = false;
  3272. }
  3273. if (apiRes.status === 403) {
  3274. this.loading = false;
  3275. this.show = false;
  3276. window.open(`https://www.v2ex.com/t/${post.id}?p=1&script=0`, "_black");
  3277. return;
  3278. }
  3279. if (apiRes.redirected) {
  3280. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "没有权限" });
  3281. return this.loading = false;
  3282. }
  3283. let htmlText = await apiRes.text();
  3284. let hasPermission = htmlText.search("你要查看的页面需要先登录");
  3285. if (hasPermission > -1) {
  3286. eventBus.emit(CMD$1.SHOW_MSG, { type: "error", text: "你要查看的页面需要先登录" });
  3287. return this.loading = false;
  3288. }
  3289. let bodyText = htmlText.match(/<body[^>]*>([\s\S]+?)<\/body>/g);
  3290. let body = $(bodyText[0]);
  3291. this.current = await window.parse.getPostDetail(this.current, body, htmlText);
  3292. if (this.current.replyList.length) {
  3293. let index2 = this.list.findIndex((v) => v.id == post.id);
  3294. if (index2 > -1) {
  3295. this.list[index2].replyList = this.current.replyList;
  3296. this.list[index2].nestedReplies = this.current.nestedReplies;
  3297. this.list[index2].once = this.current.once;
  3298. this.list[index2].createDate = this.current.createDate;
  3299. } else {
  3300. this.list.push(this.clone(this.current));
  3301. }
  3302. }
  3303. this.loading = false;
  3304. if (!alreadyHasReply) {
  3305. vue.nextTick(() => {
  3306. this.$refs.postDetail.jumpLastRead(this.current.read.floor);
  3307. });
  3308. }
  3309. console.log("当前帖子", this.current);
  3310. }
  3311. }
  3312. };
  3313. const _hoisted_1 = {
  3314. key: 0,
  3315. class: "nav flex flex-end"
  3316. };
  3317. const _hoisted_2 = {
  3318. key: 1,
  3319. class: "my-box flex f14 open-post",
  3320. style: { "margin": "2rem 0 0 0", "padding": "1rem" }
  3321. };
  3322. const _hoisted_3 = { class: "flex" };
  3323. function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  3324. const _component_Setting = vue.resolveComponent("Setting");
  3325. const _component_TagModal = vue.resolveComponent("TagModal");
  3326. const _component_PostDetail = vue.resolveComponent("PostDetail");
  3327. const _component_Base64Tooltip = vue.resolveComponent("Base64Tooltip");
  3328. const _component_MsgModal = vue.resolveComponent("MsgModal");
  3329. return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
  3330. vue.createVNode(_component_Setting, {
  3331. modelValue: $data.config,
  3332. "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $data.config = $event),
  3333. show: $data.configModal.show,
  3334. "onUpdate:show": _cache[1] || (_cache[1] = ($event) => $data.configModal.show = $event)
  3335. }, null, 8, ["modelValue", "show"]),
  3336. vue.createVNode(_component_TagModal, {
  3337. tags: $data.tags,
  3338. "onUpdate:tags": _cache[2] || (_cache[2] = ($event) => $data.tags = $event)
  3339. }, null, 8, ["tags"]),
  3340. vue.createVNode(_component_PostDetail, {
  3341. modelValue: $data.show,
  3342. "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.show = $event),
  3343. ref: "postDetail",
  3344. displayType: $data.config.commentDisplayType,
  3345. "onUpdate:displayType": _cache[4] || (_cache[4] = ($event) => $data.config.commentDisplayType = $event),
  3346. onSaveReadList: $options.saveReadList,
  3347. loading: $data.loading
  3348. }, null, 8, ["modelValue", "displayType", "onSaveReadList", "loading"]),
  3349. vue.createVNode(_component_Base64Tooltip),
  3350. vue.createVNode(_component_MsgModal),
  3351. !$data.stopMe && $data.config.showToolbar ? (vue.openBlock(), vue.createElementBlock("div", {
  3352. key: 0,
  3353. class: vue.normalizeClass(["toolbar", [$data.isNight ? "isNight" : "", $data.config["viewType"]]])
  3354. }, [
  3355. $options.isList ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
  3356. vue.createElementVNode("div", {
  3357. class: vue.normalizeClass(["radio-group2", { isNight: $data.isNight }])
  3358. }, [
  3359. vue.createElementVNode("div", {
  3360. class: vue.normalizeClass(["radio", $data.config.viewType === "table" ? "active" : ""]),
  3361. onClick: _cache[5] || (_cache[5] = ($event) => $data.config.viewType = "table")
  3362. }, "表格 ", 2),
  3363. vue.createElementVNode("div", {
  3364. class: vue.normalizeClass(["radio", $data.config.viewType === "card" ? "active" : ""]),
  3365. onClick: _cache[6] || (_cache[6] = ($event) => $data.config.viewType = "card")
  3366. }, "卡片 ", 2)
  3367. ], 2)
  3368. ])) : vue.createCommentVNode("", true),
  3369. !$options.isList && !$data.show ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2, [
  3370. vue.createElementVNode("div", _hoisted_3, [
  3371. vue.createTextVNode(" 默认显示楼中楼 : "),
  3372. vue.createElementVNode("div", {
  3373. class: vue.normalizeClass(["switch light", { active: $data.config.autoOpenDetail }]),
  3374. onClick: _cache[7] || (_cache[7] = ($event) => $data.config.autoOpenDetail = !$data.config.autoOpenDetail)
  3375. }, null, 2)
  3376. ]),
  3377. vue.createElementVNode("div", {
  3378. class: vue.normalizeClass(["button light", { loading: $data.loading, isNight: $data.isNight }]),
  3379. onClick: _cache[8] || (_cache[8] = (...args) => $options.showPost && $options.showPost(...args))
  3380. }, " 点击显示楼中楼 ", 2)
  3381. ])) : vue.createCommentVNode("", true)
  3382. ], 2)) : vue.createCommentVNode("", true)
  3383. ], 64);
  3384. }
  3385. const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-3fb4d7c1"]]);
  3386. var _GM_notification = /* @__PURE__ */ (() => typeof GM_notification != "undefined" ? GM_notification : void 0)();
  3387. var _GM_openInTab = /* @__PURE__ */ (() => typeof GM_openInTab != "undefined" ? GM_openInTab : void 0)();
  3388. var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
  3389. let $section = document.createElement("section");
  3390. $section.id = "app";
  3391. function run() {
  3392. window.baseUrl = location.origin;
  3393. window.initPost = {
  3394. allReplyUsers: [],
  3395. content_rendered: "",
  3396. createDate: "",
  3397. fr: "",
  3398. replyList: [],
  3399. nestedReplies: [],
  3400. username: "",
  3401. member: {},
  3402. node: {},
  3403. headerTemplate: "",
  3404. title: "",
  3405. id: "",
  3406. type: "post",
  3407. once: "",
  3408. replyCount: 0,
  3409. clickCount: 0,
  3410. thankCount: 0,
  3411. collectCount: 0,
  3412. lastReadFloor: 0,
  3413. isFavorite: false,
  3414. isIgnore: false,
  3415. isThanked: false,
  3416. isReport: false
  3417. };
  3418. window.win = function() {
  3419. return window;
  3420. };
  3421. window.win().doc = window.win().document;
  3422. window.win().query = (v) => window.win().document.querySelector(v);
  3423. window.query = (v) => window.win().document.querySelector(v);
  3424. window.clone = (val) => JSON.parse(JSON.stringify(val));
  3425. window.user = {
  3426. tagPrefix: "--用户标签--",
  3427. tags: {},
  3428. tagsId: "",
  3429. username: "",
  3430. avatar: "",
  3431. readPrefix: "--已读楼层--",
  3432. readNoteItemId: "",
  3433. readList: {}
  3434. };
  3435. window.pageType = void 0;
  3436. window.pageData = { pageNo: 1 };
  3437. window.config = {
  3438. showToolbar: true,
  3439. showPreviewBtn: true,
  3440. autoOpenDetail: true,
  3441. openTag: true,
  3442. //给用户打标签
  3443. clickPostItemOpenDetail: true,
  3444. closePostDetailBySpace: true,
  3445. //点击空白处关闭详情
  3446. contentAutoCollapse: true,
  3447. //正文超长自动折叠
  3448. viewType: "table",
  3449. commentDisplayType: 0,
  3450. newTabOpen: false,
  3451. //新标签打开
  3452. base64: true,
  3453. //base功能
  3454. sov2ex: false,
  3455. postWidth: "",
  3456. showTopReply: true,
  3457. topReplyLoveMinCount: 3,
  3458. topReplyCount: 3,
  3459. autoJumpLastReadFloor: false,
  3460. rememberLastReadFloor: true
  3461. };
  3462. window.isNight = $(".Night").length === 1;
  3463. window.cb = null;
  3464. window.postList = [];
  3465. window.parse = {
  3466. //解析帖子内容
  3467. async parsePostContent(post, body, htmlText) {
  3468. let once = htmlText.match(/var once = "([\d]+)";/);
  3469. if (once && once[1]) {
  3470. post.once = once[1];
  3471. }
  3472. post.isReport = htmlText.includes("你已对本主题进行了报告");
  3473. if (!post.title || !post.content_rendered) {
  3474. let main = body.find("#Main");
  3475. let aName = main.find(".header small.gray a:nth-child(1)");
  3476. if (aName) {
  3477. post.member.username = aName[0].innerText;
  3478. }
  3479. }
  3480. let topic_buttons = body.find(".topic_buttons");
  3481. if (topic_buttons.length) {
  3482. let favoriteNode = topic_buttons.find(".tb:first");
  3483. if (favoriteNode.length) {
  3484. post.isFavorite = favoriteNode[0].innerText === "取消收藏";
  3485. }
  3486. let ignoreNode = topic_buttons.find(".tb:nth-child(3)");
  3487. if (ignoreNode.length) {
  3488. post.isIgnore = ignoreNode[0].innerText === "取消忽略";
  3489. }
  3490. let thankNode = topic_buttons.find("#topic_thank .tb");
  3491. if (!thankNode.length) {
  3492. post.isThanked = true;
  3493. }
  3494. let topic_stats = topic_buttons.find(".topic_stats");
  3495. if (topic_stats.length) {
  3496. let text = topic_stats[0].innerText;
  3497. let reg1 = text.matchAll(/([\d]+)[\s]*人收藏/g);
  3498. let collectCountReg = [...reg1];
  3499. if (collectCountReg.length) {
  3500. post.collectCount = Number(collectCountReg[0][1]);
  3501. }
  3502. let reg2 = text.matchAll(/([\d]+)[\s]*人感谢/g);
  3503. let thankCountReg = [...reg2];
  3504. if (thankCountReg.length) {
  3505. post.thankCount = Number(thankCountReg[0][1]);
  3506. }
  3507. let reg3 = text.matchAll(/([\d]+)[\s]*次点击/g);
  3508. let clickCountReg = [...reg3];
  3509. if (clickCountReg.length) {
  3510. post.clickCount = Number(clickCountReg[0][1]);
  3511. }
  3512. }
  3513. }
  3514. let header = body.find("#Main .box").first();
  3515. let temp = header.clone();
  3516. temp.find(".topic_buttons").remove();
  3517. let html = temp.html();
  3518. html = this.checkPhotoLink2Img(html);
  3519. post.headerTemplate = html;
  3520. return post;
  3521. },
  3522. //获取帖子所有回复
  3523. async getPostAllReplies(post, body, htmlText, pageNo = 1) {
  3524. var _a, _b;
  3525. if (body.find("#no-comments-yet").length) {
  3526. return post;
  3527. }
  3528. let box = body.find("#Main > .box")[1];
  3529. let cells = box.querySelectorAll(".cell");
  3530. if (cells && cells.length) {
  3531. post.fr = cells[0].querySelector(".cell .fr").innerHTML;
  3532. cells = Array.from(cells);
  3533. }
  3534. let snow = cells[0].querySelector(".snow");
  3535. post.createDate = ((_b = (_a = snow == null ? void 0 : snow.nextSibling) == null ? void 0 : _a.nodeValue) == null ? void 0 : _b.trim()) || "";
  3536. let repliesMap = [];
  3537. if (cells[1].id) {
  3538. repliesMap.push({ i: pageNo, replyList: this.parsePageReplies(cells.slice(1)) });
  3539. let replyList = this.getAllReply(repliesMap);
  3540. post.replyList = replyList;
  3541. post.replyCount = replyList.length;
  3542. post.allReplyUsers = Array.from(new Set(replyList.map((v) => v.username)));
  3543. let nestedList = this.createNestedList(replyList, post.allReplyUsers);
  3544. if (nestedList)
  3545. post.nestedReplies = nestedList;
  3546. return post;
  3547. } else {
  3548. let promiseList = [];
  3549. return new Promise((resolve, reject) => {
  3550. repliesMap.push({ i: pageNo, replyList: this.parsePageReplies(cells.slice(2, cells.length - 1)) });
  3551. let pages = cells[1].querySelectorAll("a");
  3552. pages = Array.from(pages);
  3553. let url = window.baseUrl + "/t/" + post.id;
  3554. for (let i = 0; i < pages.length; i++) {
  3555. let currentPageNo = Number(pages[i].innerText);
  3556. if (currentPageNo == pageNo)
  3557. continue;
  3558. promiseList.push(this.fetchPostOtherPageReplies(url + "?p=" + currentPageNo, currentPageNo));
  3559. }
  3560. Promise.allSettled(promiseList).then(
  3561. (results) => {
  3562. results.filter((result) => result.status === "fulfilled").map((v) => repliesMap.push(v.value));
  3563. let replyList = this.getAllReply(repliesMap);
  3564. post.replyList = replyList;
  3565. post.replyCount = replyList.length;
  3566. post.allReplyUsers = Array.from(new Set(replyList.map((v) => v.username)));
  3567. let nestedList = this.createNestedList(replyList, post.allReplyUsers);
  3568. if (nestedList)
  3569. post.nestedReplies = nestedList;
  3570. resolve(post);
  3571. }
  3572. );
  3573. });
  3574. }
  3575. },
  3576. //请求帖子其他页的回复
  3577. fetchPostOtherPageReplies(href, pageNo) {
  3578. return new Promise((resolve) => {
  3579. $.get(href).then((res) => {
  3580. let s = res.match(/<body[^>]*>([\s\S]+?)<\/body>/g);
  3581. let box = $(s[0]).find("#Main .box")[1];
  3582. let cells = box.querySelectorAll(".cell");
  3583. cells = Array.from(cells);
  3584. resolve({ i: pageNo, replyList: this.parsePageReplies(cells.slice(2, cells.length - 1)) });
  3585. }).catch((r) => {
  3586. if (r.status === 403) {
  3587. cbChecker({ type: "restorePost", value: null });
  3588. }
  3589. });
  3590. });
  3591. },
  3592. //解析页面的回复
  3593. parsePageReplies(nodes) {
  3594. let replyList = [];
  3595. nodes.forEach((node, index2) => {
  3596. if (!node.id)
  3597. return;
  3598. let item = {
  3599. level: 0,
  3600. thankCount: 0,
  3601. isThanked: false,
  3602. isOp: false,
  3603. id: node.id.replace("r_", "")
  3604. };
  3605. let reply_content = node.querySelector(".reply_content");
  3606. item.reply_content = this.checkPhotoLink2Img(reply_content.innerHTML);
  3607. item.reply_text = reply_content.textContent;
  3608. let { users, floor } = this.parseReplyContent(item.reply_content);
  3609. item.replyUsers = users;
  3610. item.replyFloor = floor;
  3611. let ago = node.querySelector(".ago");
  3612. item.date = ago.textContent;
  3613. let userNode = node.querySelector("strong a");
  3614. item.username = userNode.textContent;
  3615. let avatar = node.querySelector("td img");
  3616. item.avatar = avatar.src;
  3617. let no = node.querySelector(".no");
  3618. item.floor = Number(no.textContent);
  3619. let thank_area = node.querySelector(".thank_area");
  3620. if (thank_area) {
  3621. item.isThanked = thank_area.classList.contains("thanked");
  3622. }
  3623. let small = node.querySelector(".small");
  3624. if (small) {
  3625. item.thankCount = Number(small.textContent);
  3626. }
  3627. let op = node.querySelector(".op");
  3628. if (op) {
  3629. item.isOp = true;
  3630. }
  3631. let mod = node.querySelector(".mod");
  3632. if (mod) {
  3633. item.isMod = true;
  3634. }
  3635. replyList.push(item);
  3636. });
  3637. return replyList;
  3638. },
  3639. //解析回复内容,解析出@用户,回复楼层。用于后续生成嵌套楼层
  3640. parseReplyContent(str) {
  3641. if (!str)
  3642. return;
  3643. let users = [];
  3644. let getUsername = (userStr) => {
  3645. let endIndex = userStr.indexOf('">');
  3646. if (endIndex > -1) {
  3647. let user = userStr.substring(0, endIndex);
  3648. if (!users.find((i) => i === user)) {
  3649. users.push(user);
  3650. }
  3651. }
  3652. };
  3653. let userReg = /@<a href="\/member\/([\s\S]+?)<\/a>/g;
  3654. let has = str.matchAll(userReg);
  3655. let res2 = [...has];
  3656. if (res2.length > 1) {
  3657. res2.map((item) => {
  3658. getUsername(item[1]);
  3659. });
  3660. }
  3661. if (res2.length === 1) {
  3662. getUsername(res2[0][1]);
  3663. }
  3664. let floor = -1;
  3665. if (users.length === 1) {
  3666. let floorReg = /@<a href="\/member\/[\s\S]+?<\/a>[\s]+#([\d]+)/g;
  3667. let hasFloor = str.matchAll(floorReg);
  3668. let res = [...hasFloor];
  3669. if (res.length) {
  3670. floor = Number(res[0][1]);
  3671. }
  3672. }
  3673. return { users, floor };
  3674. },
  3675. //获取帖子详情
  3676. async getPostDetail(post, body, htmlText, pageNo = 1) {
  3677. post = await this.parsePostContent(post, body, htmlText);
  3678. return await this.getPostAllReplies(post, body, htmlText, pageNo);
  3679. },
  3680. getAllReply(repliesMap = []) {
  3681. return repliesMap.sort((a, b) => a.i - b.i).reduce((pre, i) => {
  3682. pre = pre.concat(i.replyList);
  3683. return pre;
  3684. }, []);
  3685. },
  3686. //生成嵌套回复
  3687. createNestedList(allList = []) {
  3688. if (!allList.length)
  3689. return [];
  3690. if (Date.now() - window.win().lastCallDate < 1e3) {
  3691. return false;
  3692. }
  3693. let list = window.clone(allList);
  3694. let nestedList = [];
  3695. list.map((item, index2) => {
  3696. let startList = list.slice(0, index2);
  3697. let startReplyUsers = Array.from(new Set(startList.map((v) => v.username)));
  3698. let endList = list.slice(index2 + 1);
  3699. if (index2 === 0) {
  3700. nestedList.push(this.findChildren(item, endList, list));
  3701. } else {
  3702. if (!item.isUse) {
  3703. let isOneLevelReply = false;
  3704. if (item.replyUsers.length) {
  3705. if (item.replyUsers.length > 1) {
  3706. isOneLevelReply = true;
  3707. } else {
  3708. isOneLevelReply = !startReplyUsers.find((v) => v === item.replyUsers[0]);
  3709. }
  3710. } else {
  3711. isOneLevelReply = true;
  3712. }
  3713. if (isOneLevelReply) {
  3714. item.level === 0;
  3715. nestedList.push(this.findChildren(item, endList, list));
  3716. }
  3717. }
  3718. }
  3719. });
  3720. window.win().lastCallDate = Date.now();
  3721. return nestedList;
  3722. },
  3723. //查找子回复
  3724. findChildren(item, endList, all) {
  3725. const fn = (child, endList2, parent) => {
  3726. child.level = parent.level + 1;
  3727. let rIndex = all.findIndex((v) => v.floor === child.floor);
  3728. if (rIndex > -1) {
  3729. all[rIndex].isUse = true;
  3730. }
  3731. parent.children.push(this.findChildren(child, endList2, all));
  3732. };
  3733. item.children = [];
  3734. let floorReplyList = [];
  3735. for (let i = 0; i < endList.length; i++) {
  3736. let currentItem = endList[i];
  3737. if (currentItem.isUse)
  3738. continue;
  3739. if (currentItem.replyFloor === item.floor) {
  3740. if (currentItem.replyUsers.length === 1 && currentItem.replyUsers[0] === item.username) {
  3741. currentItem.isUse = true;
  3742. floorReplyList.push({ endList: endList.slice(i + 1), currentItem });
  3743. } else {
  3744. currentItem.isWrong = true;
  3745. }
  3746. }
  3747. }
  3748. floorReplyList.reverse().map(({ currentItem, endList: endList2 }) => {
  3749. fn(currentItem, endList2, item);
  3750. });
  3751. let nextMeIndex = endList.findIndex((v) => {
  3752. var _a;
  3753. return v.username === item.username && ((_a = v.replyUsers) == null ? void 0 : _a[0]) !== item.username;
  3754. });
  3755. let findList = nextMeIndex > -1 ? endList.slice(0, nextMeIndex) : endList;
  3756. for (let i = 0; i < findList.length; i++) {
  3757. let currentItem = findList[i];
  3758. if (currentItem.isUse)
  3759. continue;
  3760. if (currentItem.replyUsers.length === 1) {
  3761. if (currentItem.replyFloor !== -1) {
  3762. if (all[currentItem.replyFloor - 1].username === currentItem.replyUsers[0]) {
  3763. continue;
  3764. }
  3765. }
  3766. let endList2 = endList.slice(i + 1);
  3767. if (currentItem.username === item.username) {
  3768. if (currentItem.replyUsers[0] === item.username) {
  3769. fn(currentItem, endList2, item);
  3770. }
  3771. break;
  3772. } else {
  3773. if (currentItem.replyUsers[0] === item.username) {
  3774. fn(currentItem, endList2, item);
  3775. }
  3776. }
  3777. } else {
  3778. if (currentItem.username === item.username)
  3779. break;
  3780. }
  3781. }
  3782. item.children = item.children.sort((a, b) => a.floor - b.floor);
  3783. return item;
  3784. },
  3785. //解析页面帖子列表
  3786. parsePagePostList(list, box) {
  3787. list.forEach((itemDom) => {
  3788. let item = window.clone(window.initPost);
  3789. let item_title = itemDom.querySelector(".item_title a");
  3790. let { href, id } = window.parse.parseA(item_title);
  3791. item.id = id;
  3792. item.href = href;
  3793. item.url = location.origin + "/api/topics/show.json?id=" + item.id;
  3794. itemDom.classList.add("post-item");
  3795. itemDom.classList.add(`id_${id}`);
  3796. itemDom.dataset["href"] = href;
  3797. itemDom.dataset["id"] = id;
  3798. window.postList.push(item);
  3799. });
  3800. Promise.allSettled(window.postList.map((item) => $.get(item.url))).then((res) => {
  3801. let ok = res.filter((r) => r.status === "fulfilled").map((v) => v.value[0]);
  3802. box.style.boxShadow = "unset";
  3803. box.style.background = "unset";
  3804. if (window.config.viewType === "card") {
  3805. list.forEach((itemDom) => itemDom.classList.add("preview"));
  3806. }
  3807. ok.map((postItem) => {
  3808. if (postItem == null ? void 0 : postItem.id) {
  3809. let itemDom = box.querySelector(`.id_${postItem.id}`);
  3810. if (window.config.showPreviewBtn) {
  3811. let td = itemDom.querySelector("td:nth-child(4)");
  3812. td.style.position = "relative";
  3813. let toggle = document.createElement("div");
  3814. toggle.dataset["id"] = postItem.id;
  3815. toggle.classList.add("toggle");
  3816. toggle.innerText = "点击展开/收起";
  3817. td.append(toggle);
  3818. }
  3819. let index2 = window.postList.findIndex((v) => v.id == postItem.id);
  3820. if (index2 > -1) {
  3821. let obj = window.postList[index2];
  3822. window.postList[index2] = Object.assign({}, obj, postItem);
  3823. if (postItem.content_rendered) {
  3824. let a = document.createElement("a");
  3825. a.href = obj.href;
  3826. a.classList.add("post-content");
  3827. let div = document.createElement("div");
  3828. div.innerHTML = postItem.content_rendered;
  3829. a.append(div);
  3830. itemDom.append(a);
  3831. }
  3832. }
  3833. }
  3834. });
  3835. cbChecker({ type: "syncData" });
  3836. });
  3837. },
  3838. parseA(a) {
  3839. let href = a.href;
  3840. let id;
  3841. if (href.includes("/t/")) {
  3842. id = href.substring(href.indexOf("/t/") + 3, href.indexOf("/t/") + 9);
  3843. }
  3844. return { href, id, title: a.innerText };
  3845. },
  3846. //创建记事本子条目
  3847. async createNoteItem(itemName) {
  3848. return new Promise(async (resolve) => {
  3849. let data = new FormData();
  3850. data.append("content", itemName);
  3851. data.append("parent_id", 0);
  3852. data.append("syntax", 0);
  3853. let apiRes = await window.win().fetch(`${window.baseUrl}/notes/new`, { method: "post", body: data });
  3854. console.log(apiRes);
  3855. if (apiRes.redirected && apiRes.status === 200) {
  3856. resolve(apiRes.url.substr(-5));
  3857. return;
  3858. }
  3859. resolve(null);
  3860. });
  3861. },
  3862. //编辑记事本子条目
  3863. async editNoteItem(val, id) {
  3864. let data = new FormData();
  3865. data.append("content", val);
  3866. data.append("syntax", 0);
  3867. let apiRes = await window.fetch(`${window.baseUrl}/notes/edit/${id}`, {
  3868. method: "post",
  3869. body: data
  3870. });
  3871. return apiRes.redirected && apiRes.status === 200;
  3872. },
  3873. //标签操作
  3874. async saveTags(val) {
  3875. return await this.editNoteItem(window.user.tagPrefix + JSON.stringify(val), window.user.tagsId);
  3876. },
  3877. //已读楼层操作
  3878. async saveReadList(val) {
  3879. return await this.editNoteItem(window.user.readPrefix + JSON.stringify(val), window.user.readNoteItemId);
  3880. },
  3881. //图片链接转Img标签
  3882. checkPhotoLink2Img(str) {
  3883. if (!str)
  3884. return;
  3885. try {
  3886. let imgWebs = [
  3887. /<a((?!<a).)*href="https?:\/\/((?!<a).)*imgur.com((?!<a).)*>(((?!<a).)*)<\/a>/g,
  3888. /<a((?!<a).)*href="https?:\/\/((?!<a).)*\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)((?!<a).)*>(((?!<a).)*)<\/a>/g
  3889. ];
  3890. imgWebs.map((v, i) => {
  3891. let has = str.matchAll(v);
  3892. let res2 = [...has];
  3893. res2.map((r) => {
  3894. let p = i === 0 ? r[4] : r[5];
  3895. if (p) {
  3896. let link = p.toLowerCase();
  3897. let src = p;
  3898. if (link.includes(".png") || link.includes(".jpg") || link.includes(".jpeg") || link.includes(".gif")) {
  3899. } else {
  3900. src = p + ".png";
  3901. }
  3902. str = str.replace(r[0], `<img src="${src}" data-originUrl="${p}" data-notice="这个img标签由v2ex-超级增强脚本解析" style="max-width: 100%">`);
  3903. }
  3904. });
  3905. });
  3906. } catch (e) {
  3907. console.log("正则解析html里面的a标签的图片链接出错了");
  3908. }
  3909. return str;
  3910. }
  3911. };
  3912. async function sleep(time) {
  3913. return new Promise((resolve) => {
  3914. setTimeout(resolve, time);
  3915. });
  3916. }
  3917. async function cbChecker(val, count = 0) {
  3918. if (window.cb) {
  3919. window.cb(val);
  3920. } else {
  3921. while (!window.cb && count < 20) {
  3922. await sleep(500);
  3923. count++;
  3924. }
  3925. window.cb && window.cb(val);
  3926. }
  3927. }
  3928. function feedback() {
  3929. _GM_openInTab("https://github.com/zyronon/v2ex-script/discussions/", {
  3930. active: true,
  3931. insert: true,
  3932. setParent: true
  3933. });
  3934. }
  3935. function initMonkeyMenu() {
  3936. try {
  3937. _GM_registerMenuCommand("脚本设置", function(event) {
  3938. cbChecker({ type: "openSetting" });
  3939. });
  3940. _GM_registerMenuCommand("💬 反馈 & 建议", feedback);
  3941. } catch (e) {
  3942. console.error("无法使用Tampermonkey");
  3943. }
  3944. }
  3945. function initStyle() {
  3946. let style2 = `
  3947. html, body {
  3948. font-size: 62.5%;
  3949. }
  3950.  
  3951. #Wrapper {
  3952. height: unset !important;
  3953. width: unset !important;
  3954. }
  3955.  
  3956. #Wrapper > .content {
  3957. height: unset !important;
  3958. width: unset !important;
  3959. }
  3960.  
  3961. .post-item {
  3962. background: white;
  3963. }
  3964.  
  3965. .post-item > .post-content {
  3966. height: 0;
  3967. margin-top: 0;
  3968. }
  3969.  
  3970. .post-item:hover .toggle {
  3971. display: flex;
  3972. }
  3973.  
  3974. .toggle {
  3975. position: absolute;
  3976. right: 0;
  3977. top: 0.5rem;
  3978. width: 9rem;
  3979. height: 100%;
  3980. display: flex;
  3981. justify-content: flex-end;
  3982. align-items: flex-end;
  3983. cursor: pointer;
  3984. font-size: 1.2rem;
  3985. color: #ccc;
  3986. display: none;
  3987. }
  3988.  
  3989. .preview {
  3990. margin: 1rem 0;
  3991. border: 1px solid #e2e2e2;
  3992. border-radius: 0.4rem;
  3993. cursor: pointer;
  3994. }
  3995.  
  3996. .preview:hover {
  3997. border: 1px solid #968b8b;
  3998. }
  3999.  
  4000. .preview > .post-content {
  4001. height: unset !important;
  4002. margin-top: 0.5rem !important;
  4003. }
  4004.  
  4005. .preview .topic-link:link {
  4006. color: black !important;
  4007. }
  4008.  
  4009. .post-content {
  4010. margin-top: 0.5rem;
  4011. display: block;
  4012. max-height: 20rem;
  4013. overflow: hidden;
  4014. text-decoration: unset !important;
  4015. line-break: anywhere;
  4016. }
  4017.  
  4018. .post-content:link {
  4019. color: #494949;
  4020. }
  4021.  
  4022.  
  4023. .post-content:visited {
  4024. color: #afb9c1 !important;
  4025. }
  4026.  
  4027. .Night .post-item {
  4028. background: #18222d !important;
  4029. }
  4030.  
  4031. .Night .preview {
  4032. border: 1px solid #3b536e;
  4033. }
  4034.  
  4035. .Night .preview > .post-content:link {
  4036. color: #d1d5d9;
  4037. }
  4038.  
  4039. .Night .preview > .post-content:visited {
  4040. color: #393f4e !important;
  4041. }
  4042.  
  4043. .Night .preview .topic-link:link {
  4044. color: #c0dbff !important;
  4045. }
  4046.  
  4047. `;
  4048. let addStyle2 = document.createElement("style");
  4049. addStyle2.rel = "stylesheet";
  4050. addStyle2.type = "text/css";
  4051. addStyle2.innerHTML = style2;
  4052. $(window.win().doc.head).append(addStyle2);
  4053. }
  4054. function qianDao() {
  4055. let timeNow = (/* @__PURE__ */ new Date()).getUTCFullYear() + "/" + ((/* @__PURE__ */ new Date()).getUTCMonth() + 1) + "/" + (/* @__PURE__ */ new Date()).getUTCDate();
  4056. if (window.pageType === PageType.Home) {
  4057. let qiandao = window.query('.box .inner a[href="/mission/daily"]');
  4058. if (qiandao) {
  4059. qianDao_(qiandao, timeNow);
  4060. } else if (window.win().doc.getElementById("gift_v2excellent")) {
  4061. window.win().doc.getElementById("gift_v2excellent").click();
  4062. localStorage.setItem("menu_clockInTime", timeNow);
  4063. console.info("[V2EX - 超级增强] 自动签到完成!");
  4064. } else {
  4065. console.info("[V2EX - 超级增强] 自动签到完成!");
  4066. }
  4067. } else {
  4068. let timeOld = localStorage.getItem("menu_clockInTime");
  4069. if (!timeOld || timeOld != timeNow) {
  4070. qianDaoStatus_(timeNow);
  4071. } else {
  4072. console.info("[V2EX - 超级增强] 自动签到完成!");
  4073. }
  4074. }
  4075. }
  4076. function qianDao_(qiandao, timeNow) {
  4077. let url = window.baseUrl + "/mission/daily/redeem?" + RegExp("once\\=(\\d+)").exec(document.querySelector("div#Top .tools, #menu-body").innerHTML)[0];
  4078. console.log("url", url);
  4079. $.get(url).then((r) => {
  4080. let bodyText = r.match(/<body[^>]*>([\s\S]+?)<\/body>/g);
  4081. let html = $(bodyText[0]);
  4082. if (html.find("li.fa.fa-ok-sign").length) {
  4083. html = html.find("#Main").text().match(/已连续登录 (\d+?) 天/)[0];
  4084. localStorage.setItem("menu_clockInTime", timeNow);
  4085. console.info("[V2EX - 超级增强] 自动签到完成!");
  4086. if (qiandao) {
  4087. qiandao.textContent = `自动签到完成!${html}`;
  4088. qiandao.href = "javascript:void(0);";
  4089. }
  4090. } else {
  4091. _GM_notification({
  4092. text: "自动签到失败!请访问 V2EX 首页试试。\n如果连续几天都签到失败,请联系作者解决!",
  4093. timeout: 4e3,
  4094. onclick() {
  4095. feedback();
  4096. }
  4097. });
  4098. console.warn("[V2EX 增强] 自动签到失败!请访问 V2EX 首页试试。如果连续几天都签到失败,请联系作者解决!");
  4099. if (qiandao)
  4100. qiandao.textContent = "自动签到失败!请尝试手动签到!";
  4101. }
  4102. });
  4103. }
  4104. function qianDaoStatus_(timeNow) {
  4105. $.get(window.baseUrl + "/mission/daily").then((r) => {
  4106. let bodyText = r.match(/<body[^>]*>([\s\S]+?)<\/body>/g);
  4107. let html = $(bodyText[0]);
  4108. if (html.find('input[value^="领取"]').length) {
  4109. qianDao_(null, timeNow);
  4110. } else {
  4111. console.info("[V2EX 增强] 已经签过到了。");
  4112. localStorage.setItem("menu_clockInTime", timeNow);
  4113. }
  4114. });
  4115. }
  4116. function checkPageType() {
  4117. let location2 = window.win().location;
  4118. if (location2.pathname === "/") {
  4119. window.pageType = PageType.Home;
  4120. } else if (location2.href.match(/.com\/?tab=/)) {
  4121. window.pageType = PageType.Home;
  4122. } else if (location2.href.match(/.com\/go\//)) {
  4123. if (!location2.href.includes("/links")) {
  4124. window.pageType = PageType.Node;
  4125. }
  4126. } else if (location2.href.match(/.com\/recent/)) {
  4127. window.pageType = PageType.Home;
  4128. } else {
  4129. let r = location2.href.match(/.com\/t\/([\d]+)/);
  4130. if (r) {
  4131. window.pageType = PageType.Post;
  4132. window.pageData.id = r[1];
  4133. if (location2.search) {
  4134. let pr = location2.href.match(/\?p=([\d]+)/);
  4135. if (pr)
  4136. window.pageData.pageNo = Number(pr[1]);
  4137. }
  4138. }
  4139. }
  4140. }
  4141. function getNoteItemContent(id, prefix) {
  4142. return new Promise((resolve, reject) => {
  4143. $.get(window.baseUrl + "/notes/edit/" + id).then((r2) => {
  4144. let bodyText = r2.match(/<body[^>]*>([\s\S]+?)<\/body>/g);
  4145. let body = $(bodyText[0]);
  4146. let text = body.find(".note_editor").text();
  4147. if (text === prefix) {
  4148. resolve({});
  4149. } else {
  4150. let tagJson = text.substring(prefix.length);
  4151. try {
  4152. resolve(JSON.parse(tagJson));
  4153. } catch (e) {
  4154. console.log("tage", tagJson);
  4155. resolve({});
  4156. }
  4157. }
  4158. });
  4159. });
  4160. }
  4161. async function initNoteData() {
  4162. $.get(window.baseUrl + "/notes").then(async (r) => {
  4163. let bodyText = r.match(/<body[^>]*>([\s\S]+?)<\/body>/g);
  4164. let body = $(bodyText[0]);
  4165. let items = body.find("#Main .box .note_item_title a");
  4166. if (items.length) {
  4167. let tagItem = Array.from(items).find((v) => v.innerText.includes(window.user.tagPrefix));
  4168. if (tagItem) {
  4169. window.user.tagsId = tagItem.href.substr(-5);
  4170. window.user.tags = await getNoteItemContent(window.user.tagsId, window.user.tagPrefix);
  4171. } else {
  4172. let r2 = await window.parse.createNoteItem(window.user.tagPrefix);
  4173. r2 && (window.user.tagsId = r2);
  4174. }
  4175. let readItem = Array.from(items).find((v) => v.innerText.includes(window.user.readPrefix));
  4176. if (readItem) {
  4177. window.user.readNoteItemId = readItem.href.substr(-5);
  4178. window.user.readList = await getNoteItemContent(window.user.readNoteItemId, window.user.readPrefix);
  4179. } else {
  4180. let r2 = await window.parse.createNoteItem(window.user.readPrefix);
  4181. r2 && (window.user.readNoteItemId = r2);
  4182. }
  4183. cbChecker({ type: "syncData" });
  4184. }
  4185. });
  4186. }
  4187. function initConfig() {
  4188. return new Promise((resolve) => {
  4189. let configStr = window.win().localStorage.getItem("v2ex-config");
  4190. if (configStr) {
  4191. let configObj = JSON.parse(configStr);
  4192. configObj = configObj[window.user.username ?? "default"];
  4193. if (configObj) {
  4194. window.config = Object.assign(window.config, configObj);
  4195. }
  4196. }
  4197. resolve(window.config);
  4198. });
  4199. }
  4200. function initSoV2ex() {
  4201. var $search = $("#search");
  4202. var searchEvents = $._data($search[0], "events");
  4203. console.log($search, searchEvents);
  4204. var oKeydownEvent = searchEvents["keydown"][0]["handler"];
  4205. var oInputEvent = searchEvents["input"][0]["handler"];
  4206. $search.attr("placeholder", "sov2ex");
  4207. $search.unbind("keydown", oKeydownEvent);
  4208. $search.unbind("input", oInputEvent);
  4209. $search.on("input", function(e) {
  4210. oInputEvent(e);
  4211. $(".search-item:last").attr("href", "https://www.sov2ex.com/?q=" + $search.val()).text("sov2ex " + $search.val());
  4212. });
  4213. $search.keydown(function(e) {
  4214. if (e.code == "Enter" || e.code == "NumpadEnter" || e.keyCode === 13) {
  4215. if ($(".search-item:last").is(".active")) {
  4216. $(this).val($(this).val().replace(/[#%&]/g, ""));
  4217. window.open("https://www.sov2ex.com/?q=" + $(this).val());
  4218. return 0;
  4219. }
  4220. }
  4221. oKeydownEvent(e);
  4222. });
  4223. }
  4224. function addSettingText() {
  4225. let setting = $(`<a href="javascript:void 0;" class="top">脚本设置</a>`);
  4226. setting.on("click", () => {
  4227. cbChecker({ type: "openSetting" });
  4228. });
  4229. $(".tools").prepend(setting);
  4230. }
  4231. function init() {
  4232. checkPageType();
  4233. initMonkeyMenu();
  4234. initStyle();
  4235. let top2 = document.querySelector(".tools .top:nth-child(2)");
  4236. if (top2 && top2.textContent !== "注册") {
  4237. window.user.username = top2.textContent;
  4238. window.user.avatar = $("#Rightbar .box .avatar").attr("src");
  4239. initNoteData();
  4240. try {
  4241. qianDao();
  4242. } catch (e) {
  4243. console.log("签到失败");
  4244. }
  4245. }
  4246. addSettingText();
  4247. initConfig().then((r) => {
  4248. if (window.config.sov2ex) {
  4249. setTimeout(initSoV2ex, 1e3);
  4250. }
  4251. });
  4252. let box;
  4253. let list;
  4254. switch (window.pageType) {
  4255. case PageType.Node:
  4256. box = window.win().doc.querySelectorAll("#Wrapper #Main .box");
  4257. let topics = box[1].querySelector("#TopicsNode");
  4258. list = topics.querySelectorAll(".cell");
  4259. list[0].before($section);
  4260. window.parse.parsePagePostList(list, box[1]);
  4261. break;
  4262. case PageType.Home:
  4263. box = document.querySelector("#Wrapper #Main .box");
  4264. list = box.querySelectorAll(".item");
  4265. list[0].before($section);
  4266. window.parse.parsePagePostList(list, box);
  4267. break;
  4268. case PageType.Post:
  4269. if (window.config.postWidth) {
  4270. let Main = $("#Main");
  4271. Main.css({
  4272. "width": window.config.postWidth,
  4273. margin: "unset"
  4274. });
  4275. $("#Wrapper > .content").css({
  4276. "max-width": "unset",
  4277. display: "flex",
  4278. "justify-content": "center",
  4279. gap: "20px"
  4280. });
  4281. Main.after($("#Rightbar"));
  4282. }
  4283. box = document.querySelector("#Wrapper #Main .box");
  4284. box.after($section);
  4285. let post = Object.assign({}, window.clone(window.initPost), { id: window.pageData.id });
  4286. let body = $(window.win().doc.body);
  4287. let htmlText = window.win().doc.documentElement.outerHTML;
  4288. window.parse.parsePostContent(
  4289. post,
  4290. body,
  4291. htmlText
  4292. ).then(async (res) => {
  4293. await cbChecker({ type: "postContent", value: res }, 0);
  4294. });
  4295. window.parse.getPostAllReplies(
  4296. post,
  4297. body,
  4298. htmlText,
  4299. window.pageData.pageNo
  4300. ).then(async (res) => {
  4301. await cbChecker({ type: "postReplies", value: res }, 0);
  4302. });
  4303. break;
  4304. default:
  4305. console.error("未知页面");
  4306. break;
  4307. }
  4308. }
  4309. window.canParseV2exPage = !window.location.href.includes("script=0");
  4310. if (window.canParseV2exPage) {
  4311. init();
  4312. } else {
  4313. alert("脚本无法查看此主题,已为您单独打开此主题");
  4314. }
  4315. }
  4316. run();
  4317. let vueApp = vue.createApp(App);
  4318. vueApp.config.unwrapInjectedRef = true;
  4319. vueApp.mount($section);
  4320.  
  4321. })(Vue);