Browse Source

优化链接处理,确保未设置target属性的链接默认打开在新窗口

懒得勤快 3 days ago
parent
commit
05b731bec8

+ 5 - 0
src/Masuit.MyBlogs.Core/wwwroot/Content/common/reset.css

@@ -142,4 +142,9 @@ table {
 
 iframe {
   width: 100%;
+}
+
+img {
+  max-width: 100%;
+  height: auto;
 }

+ 18 - 4
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.js

@@ -577,8 +577,15 @@ createApp({
             paragraph: { 'p': '', 'h4': '', 'h5': '', 'h6': '' },
             autoHeightEnabled: true
           });
-          ue2.addListener('contentChange', () => {
-            this.reply.Content = ue2.getContent();
+          window.ue2.addListener('contentChange', () => {
+            this.reply.Content = window.ue2.getContent();
+            const links = window.ue2.body.querySelectorAll('a');
+            for (let i = 0; i < links.length; i++) {
+              // 如果链接没有设置target属性,则设置为 _blank
+              if (!links[i].getAttribute('target')) {
+                links[i].setAttribute('target', '_blank');
+              }
+            }
           });
         });
       } else {
@@ -638,8 +645,15 @@ createApp({
         paragraph: { 'p': '', 'h4': '', 'h5': '', 'h6': '' },
         autoHeightEnabled: true
       });
-      ue.addListener('contentChange', () => {
-        this.msg.Content = ue.getContent();
+      window.ue.addListener('contentChange', () => {
+        this.msg.Content = window.ue.getContent();
+        const links = window.ue.body.querySelectorAll('a');
+        for (let i = 0; i < links.length; i++) {
+          // 如果链接没有设置target属性,则设置为 _blank
+          if (!links[i].getAttribute('target')) {
+            links[i].setAttribute('target', '_blank');
+          }
+        }
       });
     }
     setTimeout(() => {

+ 19 - 6
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/leavemsg.js

@@ -1,5 +1,4 @@
-
-const { createApp, ref, onMounted, watch, computed, defineComponent, nextTick } = Vue;
+const { createApp, ref, onMounted, watch, computed, defineComponent, nextTick } = Vue;
 const { createDiscreteApi } = naive;
 const MessageReplies = defineComponent({
   name: 'MessageReplies',
@@ -351,8 +350,15 @@ createApp({
             paragraph: { 'p': '', 'h4': '', 'h5': '', 'h6': '' },
             autoHeightEnabled: true
           });
-          ue2.addListener('contentChange', () => {
-            this.reply.Content = ue2.getContent();
+          window.ue2.addListener('contentChange', () => {
+            this.reply.Content = window.ue2.getContent();
+            const links = window.ue2.body.querySelectorAll('a');
+            for (let i = 0; i < links.length; i++) {
+              // 如果链接没有设置target属性,则设置为 _blank
+              if (!links[i].getAttribute('target')) {
+                links[i].setAttribute('target', '_blank');
+              }
+            }
           });
         });
       } else {
@@ -402,8 +408,15 @@ createApp({
         paragraph: { 'p': '', 'h4': '', 'h5': '', 'h6': '' },
         autoHeightEnabled: true
       });
-      ue.addListener('contentChange', () => {
-        this.msg.Content = ue.getContent();
+      window.ue.addListener('contentChange', () => {
+        this.msg.Content = window.ue.getContent();
+        const links = window.ue.body.querySelectorAll('a');
+        for (let i = 0; i < links.length; i++) {
+          // 如果链接没有设置target属性,则设置为 _blank
+          if (!links[i].getAttribute('target')) {
+            links[i].setAttribute('target', '_blank');
+          }
+        }
       });
     }
   }

+ 154 - 147
src/Masuit.MyBlogs.Core/wwwroot/Scripts/publish/publish.js

@@ -7,162 +7,169 @@ const { message, dialog } = createDiscreteApi(["message", "dialog"]);
 window.message = message;
 window.dialog = dialog;
 createApp({
-    setup() {
-        const post = ref({ CategoryId: 1 });
-        const categories = ref([]);
-        const disableGetcode = ref(false);
-        const codeMsg = ref("获取验证码");
-        return {
-            post,
-            categories,
-            disableGetcode,
-            codeMsg
-        };
-    },
-    methods: {
-        flattenCategories(categories, parentName = '') {
-            const result = []
+  setup() {
+    const post = ref({ CategoryId: 1 });
+    const categories = ref([]);
+    const disableGetcode = ref(false);
+    const codeMsg = ref("获取验证码");
+    return {
+      post,
+      categories,
+      disableGetcode,
+      codeMsg
+    };
+  },
+  methods: {
+    flattenCategories(categories, parentName = '') {
+      const result = []
 
-            for (const category of categories) {
-                // 构建当前分类的完整名称
-                const fullName = parentName ? `${parentName}/${category.Name}` : category.Name
+      for (const category of categories) {
+        // 构建当前分类的完整名称
+        const fullName = parentName ? `${parentName}/${category.Name}` : category.Name
 
-                // 添加当前分类到结果中
-                result.push({
-                    ...category,
-                    Name: fullName,
-                    OriginalName: category.Name // 保存原始名称
-                })
+        // 添加当前分类到结果中
+        result.push({
+          ...category,
+          Name: fullName,
+          OriginalName: category.Name // 保存原始名称
+        })
 
-                // 递归处理子分类
-                if (category.Children && category.Children.length > 0) {
-                    const childCategories = this.flattenCategories(category.Children, fullName)
-                    result.push(...childCategories)
-                }
-            }
+        // 递归处理子分类
+        if (category.Children && category.Children.length > 0) {
+          const childCategories = this.flattenCategories(category.Children, fullName)
+          result.push(...childCategories)
+        }
+      }
 
-            return result
-        },
-        async getCategories() {
-            const data = await axios.get("/category/getcategories").then(function (response) {
-                return response.data;
-            });
-            if (!data.Success) {
-                return;
-            }
+      return result
+    },
+    async getCategories() {
+      const data = await axios.get("/category/getcategories").then(function (response) {
+        return response.data;
+      });
+      if (!data.Success) {
+        return;
+      }
 
-            this.categories = this.flattenCategories(data.Data.sort((a, b) => (b.Id == 1) - (a.Id == 1)));
-        },
-        submit() {
-            if (this.post.Title.trim().length <= 2 || this.post.Title.trim().length > 128) {
-                message.error('文章标题必须在2到128个字符以内!');
-                return;
-            }
-            if (this.post.Author.trim().length <= 1 || this.post.Author.trim().length > 24) {
-                message.error('昵称不能少于2个字符或超过24个字符!');
-                return;
-            }
-            if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(this.post.Email.trim())) {
-                message.error('请输入正确的邮箱格式!');
-                return;
-            }
-            if (this.post.Content.length < 20 || this.post.Content.length > 1000000) {
-                message.error('文章内容过短或者超长,请修改后再提交!');
-                return;
-            }
-            axios.create({
-                headers: {
-                    'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
-                }
-            }).post("/Post/Publish", this.post).then(res => {
-                const data = res.data;
-                if (data.Success) {
-                    dialog.success({ title: '投递成功', content: data.Message })
-                    clearInterval(window.interval);
-                    localStorage.removeItem("write-post-draft");
-                    this.post = { CategoryId: 1 };
-                    ue.setContent('');
-                } else {
-                    message.error(data.Message);
-                }
-            });
-        },
-        async getcode(email) {
-            message.info('正在发送验证码,请稍候...');
-            const data = await axios.create({
-                headers: {
-                    'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
-                }
-            }).post("/validate/sendcode", {
-                //__RequestVerificationToken: document.querySelector('input[name="__RequestVerificationToken"]').value,
-                email: email
-            }).then(res => res.data);
-            if (data.Success) {
-                this.disableGetcode = true;
-                message.success('验证码发送成功,请注意查收邮件,若未收到,请检查你的邮箱地址或邮件垃圾箱!');
-                localStorage.setItem("user", JSON.stringify({ NickName: this.post.Author, Email: this.post.Email }));
-                var count = 0;
-                var timer = setInterval(() => {
-                    count++;
-                    this.codeMsg = '重新发送(' + (120 - count) + ')';
-                    if (count > 120) {
-                        clearInterval(timer);
-                        this.disableGetcode = false;
-                        this.codeMsg = '重新发送';
-                    }
-                }, 1000);
-            } else {
-                message.error(data.Message);
-                this.disableGetcode = false;
-            }
-        },
-        search() {
-            window.open("/s?wd=" + this.post.Title);
-        }
+      this.categories = this.flattenCategories(data.Data.sort((a, b) => (b.Id == 1) - (a.Id == 1)));
     },
-    created() {
-        if (window.UE) {
-            window.ue = UE.getEditor('editor', {
-                initialFrameWidth: null,
-                initialFrameHeight: document.body.offsetHeight - 200
-            });
-            ue.addListener('contentChange', () => {
-                this.post.Content = ue.getContent();
-            });
-        }
-        this.getCategories();
-        var user = JSON.parse(localStorage.getItem("user"));
-        if (user) {
-            this.post.Author = user.NickName;
-            this.post.Email = user.Email;
+    submit() {
+      if (this.post.Title.trim().length <= 2 || this.post.Title.trim().length > 128) {
+        message.error('文章标题必须在2到128个字符以内!');
+        return;
+      }
+      if (this.post.Author.trim().length <= 1 || this.post.Author.trim().length > 24) {
+        message.error('昵称不能少于2个字符或超过24个字符!');
+        return;
+      }
+      if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(this.post.Email.trim())) {
+        message.error('请输入正确的邮箱格式!');
+        return;
+      }
+      if (this.post.Content.length < 20 || this.post.Content.length > 1000000) {
+        message.error('文章内容过短或者超长,请修改后再提交!');
+        return;
+      }
+      axios.create({
+        headers: {
+          'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
         }
-        //检查草稿
-        const post = JSON.parse(localStorage.getItem("write-post-draft"));
-        if (post && post.Content) {
-            dialog.warning({
-                title: '草稿箱',
-                content: '检查到上次有未提交的草稿,是否加载?',
-                positiveText: '确定',
-                negativeText: '取消',
-                draggable: true,
-                onPositiveClick: () => {
-                    this.post = post;
-                    ue.setContent(this.post.Content);
-                    window.interval = setInterval(() => {
-                        localStorage.setItem("write-post-draft", JSON.stringify(this.post));
-                    }, 5000);
-                },
-                onNegativeClick: () => {
-                    window.interval = setInterval(() => {
-                        localStorage.setItem("write-post-draft", JSON.stringify(this.post));
-                    }, 5000);
-                }
-            });
+      }).post("/Post/Publish", this.post).then(res => {
+        const data = res.data;
+        if (data.Success) {
+          dialog.success({ title: '投递成功', content: data.Message })
+          clearInterval(window.interval);
+          localStorage.removeItem("write-post-draft");
+          this.post = { CategoryId: 1 };
+          ue.setContent('');
         } else {
-            window.interval = setInterval(() => {
-                localStorage.setItem("write-post-draft", JSON.stringify(this.post));
-            }, 5000);
+          message.error(data.Message);
+        }
+      });
+    },
+    async getcode(email) {
+      message.info('正在发送验证码,请稍候...');
+      const data = await axios.create({
+        headers: {
+          'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
         }
+      }).post("/validate/sendcode", {
+        //__RequestVerificationToken: document.querySelector('input[name="__RequestVerificationToken"]').value,
+        email: email
+      }).then(res => res.data);
+      if (data.Success) {
+        this.disableGetcode = true;
+        message.success('验证码发送成功,请注意查收邮件,若未收到,请检查你的邮箱地址或邮件垃圾箱!');
+        localStorage.setItem("user", JSON.stringify({ NickName: this.post.Author, Email: this.post.Email }));
+        var count = 0;
+        var timer = setInterval(() => {
+          count++;
+          this.codeMsg = '重新发送(' + (120 - count) + ')';
+          if (count > 120) {
+            clearInterval(timer);
+            this.disableGetcode = false;
+            this.codeMsg = '重新发送';
+          }
+        }, 1000);
+      } else {
+        message.error(data.Message);
+        this.disableGetcode = false;
+      }
     },
+    search() {
+      window.open("/s?wd=" + this.post.Title);
+    }
+  },
+  created() {
+    if (window.UE) {
+      window.ue = UE.getEditor('editor', {
+        initialFrameWidth: null,
+        initialFrameHeight: document.body.offsetHeight - 200
+      });
+      window.ue.addListener('contentChange', () => {
+        this.post.Content = window.ue.getContent();
+        var links = window.ue.body.querySelectorAll('a');
+        for (var i = 0; i < links.length; i++) {
+          // 如果链接没有设置target属性,则设置为 _blank
+          if (!links[i].getAttribute('target')) {
+            links[i].setAttribute('target', '_blank');
+          }
+        }
+      });
+    }
+    this.getCategories();
+    var user = JSON.parse(localStorage.getItem("user"));
+    if (user) {
+      this.post.Author = user.NickName;
+      this.post.Email = user.Email;
+    }
+    //检查草稿
+    const post = JSON.parse(localStorage.getItem("write-post-draft"));
+    if (post && post.Content) {
+      dialog.warning({
+        title: '草稿箱',
+        content: '检查到上次有未提交的草稿,是否加载?',
+        positiveText: '确定',
+        negativeText: '取消',
+        draggable: true,
+        onPositiveClick: () => {
+          this.post = post;
+          ue.setContent(this.post.Content);
+          window.interval = setInterval(() => {
+            localStorage.setItem("write-post-draft", JSON.stringify(this.post));
+          }, 5000);
+        },
+        onNegativeClick: () => {
+          window.interval = setInterval(() => {
+            localStorage.setItem("write-post-draft", JSON.stringify(this.post));
+          }, 5000);
+        }
+      });
+    } else {
+      window.interval = setInterval(() => {
+        localStorage.setItem("write-post-draft", JSON.stringify(this.post));
+      }, 5000);
+    }
+  },
 }).use(naive).mount('#publishApp');
 // }

+ 105 - 107
src/Masuit.MyBlogs.Core/wwwroot/UEditorPlus/dialogs/link/link.html

@@ -1,9 +1,9 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-    "http://www.w3.org/TR/html4/loose.dtd">
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+          "http://www.w3.org/TR/html4/loose.dtd">
 <html>
 <head>
     <title></title>
-    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
+    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
     <script type="text/javascript" src="../internal.js?df5a6b9e"></script>
     <style type="text/css">
         * {
@@ -32,124 +32,122 @@
     </style>
 </head>
 <body>
-<div style="padding:10px;">
-    <table>
-        <tr>
-            <td><label for="text"> <var id="lang_input_text"></var></label></td>
-            <td><input class="txt" id="text" type="text" disabled="true"/></td>
-        </tr>
-        <tr>
-            <td><label for="href"> <var id="lang_input_url"></var></label></td>
-            <td><input class="txt" id="href" type="text"/></td>
-        </tr>
-        <tr>
-            <td><label for="title"> <var id="lang_input_title"></var></label></td>
-            <td><input class="txt" id="title" type="text"/></td>
-        </tr>
-        <tr>
-            <td colspan="2">
-                <label for="target"><var id="lang_input_target"></var></label>
-                <input id="target" type="checkbox"/>
-            </td>
-        </tr>
-        <tr>
-            <td colspan="2" id="msg"></td>
-        </tr>
-    </table>
-</div>
-<script type="text/javascript">
+    <div style="padding:10px;">
+        <table>
+            <tr>
+                <td><label for="text"> <var id="lang_input_text"></var></label></td>
+                <td><input class="txt" id="text" type="text" disabled="true" /></td>
+            </tr>
+            <tr>
+                <td><label for="href"> <var id="lang_input_url"></var></label></td>
+                <td><input class="txt" id="href" type="text" /></td>
+            </tr>
+            <tr>
+                <td><label for="title"> <var id="lang_input_title"></var></label></td>
+                <td><input class="txt" id="title" type="text" /></td>
+            </tr>
+            <tr>
+                <td colspan="2">
+                    <label for="target"><var id="lang_input_target"></var></label>
+                    <input id="target" type="checkbox" />
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2" id="msg"></td>
+            </tr>
+        </table>
+    </div>
+    <script type="text/javascript">
 
-    editor.setOpt('allowLinkProtocols', ['http:', 'https:', '#', '/', 'ftp:', 'mailto:', 'tel:']);
-    var allowLinkProtocols = editor.getOpt('allowLinkProtocols');
+        editor.setOpt('allowLinkProtocols', ['http:', 'https:', '#', '/', 'ftp:', 'mailto:', 'tel:']);
+        var allowLinkProtocols = editor.getOpt('allowLinkProtocols');
 
-    var range = editor.selection.getRange(),
-        link = range.collapsed ? editor.queryCommandValue("link") : editor.selection.getStart(),
-        url,
-        text = $G('text'),
-        rangeLink = domUtils.findParentByTagName(range.getCommonAncestor(), 'a', true),
-        orgText;
+        var range = editor.selection.getRange(),
+            link = range.collapsed ? editor.queryCommandValue("link") : editor.selection.getStart(),
+            url,
+            text = $G('text'),
+            rangeLink = domUtils.findParentByTagName(range.getCommonAncestor(), 'a', true),
+            orgText;
 
-    link = domUtils.findParentByTagName(link, "a", true);
+        link = domUtils.findParentByTagName(link, "a", true);
 
-    if (link) {
-        url = utils.html(link.getAttribute('_href') || link.getAttribute('href', 2));
+        if (link) {
+            url = utils.html(link.getAttribute('_href') || link.getAttribute('href', 2));
 
-        if (rangeLink === link && !link.getElementsByTagName('img').length) {
-            text.removeAttribute('disabled');
-            orgText = text.value = link[browser.ie ? 'innerText' : 'textContent'];
-        } else {
-            text.setAttribute('disabled', 'true');
-            text.value = lang.validLink;
-        }
+            if (rangeLink === link && !link.getElementsByTagName('img').length) {
+                text.removeAttribute('disabled');
+                orgText = text.value = link[browser.ie ? 'innerText' : 'textContent'];
+            } else {
+                text.setAttribute('disabled', 'true');
+                text.value = lang.validLink;
+            }
 
-    } else {
-        if (range.collapsed) {
-            text.removeAttribute('disabled');
-            text.value = '';
         } else {
-            text.setAttribute('disabled', 'true');
-            text.value = lang.validLink;
-        }
-
-    }
-    $G("title").value = url ? link.title : "";
-    $G("href").value = url ? url : '';
-    $G("target").checked = url && link.target == "_blank" ? true : false;
-    $focus($G("href"));
-
-    function handleDialogOk() {
-        var href = $G('href').value.replace(/^\s+|\s+$/g, '');
-        if (href) {
-            if (!hrefStartWith(href, allowLinkProtocols)) {
-                href = "http://" + href;
-            }
-            var obj = {
-                'href': href,
-                'target': $G("target").checked ? "_blank" : '_self',
-                'title': $G("title").value.replace(/^\s+|\s+$/g, ''),
-                '_href': href
-            };
-            //修改链接内容的情况太特殊了,所以先做到这里了
-            //todo:情况多的时候,做到command里
-            if (orgText && text.value != orgText) {
-                link[browser.ie ? 'innerText' : 'textContent'] = obj.textValue = text.value;
-                range.selectNode(link).select()
-            }
             if (range.collapsed) {
-                obj.textValue = text.value;
+                text.removeAttribute('disabled');
+                text.value = '';
+            } else {
+                text.setAttribute('disabled', 'true');
+                text.value = lang.validLink;
             }
-            editor.execCommand('link', utils.clearEmptyAttrs(obj));
-            dialog.close();
-        }
-    }
 
-    dialog.onok = handleDialogOk;
-    $G('href').onkeydown = $G('title').onkeydown = function (evt) {
-        evt = evt || window.event;
-        if (evt.keyCode == 13) {
-            handleDialogOk();
-            return false;
-        }
-    };
-    $G('href').onblur = function () {
-        if (!hrefStartWith(this.value, allowLinkProtocols)) {
-            $G("msg").innerHTML = "<span style='color: red'>" + lang.httpPrompt + "</span>";
-        } else {
-            $G("msg").innerHTML = "";
         }
-    };
+        $G("title").value = url ? link.title : "";
+        $G("href").value = url ? url : '';
+        $G("target").checked = true;
+        $focus($G("href"));
 
-    function hrefStartWith(href, arr) {
-        href = href.replace(/^\s+|\s+$/g, '');
-        for (var i = 0, ai; ai = arr[i++];) {
-            if (href.indexOf(ai) == 0) {
-                return true;
+        function handleDialogOk() {
+            var href = $G('href').value.replace(/^\s+|\s+$/g, '');
+            if (href) {
+                if (!hrefStartWith(href, allowLinkProtocols)) {
+                    href = "http://" + href;
+                }
+                var obj = {
+                    'href': href,
+                    'target': $G("target").checked ? "_blank" : '_self',
+                    'title': $G("title").value.replace(/^\s+|\s+$/g, ''),
+                    '_href': href
+                };
+                //修改链接内容的情况太特殊了,所以先做到这里了
+                //todo:情况多的时候,做到command里
+                if (orgText && text.value != orgText) {
+                    link[browser.ie ? 'innerText' : 'textContent'] = obj.textValue = text.value;
+                    range.selectNode(link).select()
+                }
+                if (range.collapsed) {
+                    obj.textValue = text.value;
+                }
+                editor.execCommand('link', utils.clearEmptyAttrs(obj));
+                dialog.close();
             }
         }
-        return false;
-    }
 
+        dialog.onok = handleDialogOk;
+        $G('href').onkeydown = $G('title').onkeydown = function (evt) {
+            evt = evt || window.event;
+            if (evt.keyCode == 13) {
+                handleDialogOk();
+                return false;
+            }
+        };
+        $G('href').onblur = function () {
+            if (!hrefStartWith(this.value, allowLinkProtocols)) {
+                $G("msg").innerHTML = "<span style='color: red'>" + lang.httpPrompt + "</span>";
+            } else {
+                $G("msg").innerHTML = "";
+            }
+        };
 
-</script>
+        function hrefStartWith(href, arr) {
+            href = href.replace(/^\s+|\s+$/g, '');
+            for (var i = 0, ai; ai = arr[i++];) {
+                if (href.indexOf(ai) == 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    </script>
 </body>
-</html>
+</html>