Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b40ac2e941 | ||
|
|
a2f34ff86d | ||
|
|
a0aab71359 | ||
|
|
0224910059 | ||
|
|
2275393523 | ||
|
|
1501a52f35 | ||
|
|
b4c2459df2 | ||
|
|
da6fea3b8c | ||
|
|
f2b1fc2edd | ||
|
|
4d18799790 | ||
|
|
6e4570bd4a | ||
|
|
494a9881e3 |
No files matched your search
@@ -32,9 +32,9 @@
|
||||
|
||||
## 食用
|
||||
|
||||
> 生产环境版下载:[Artalk.js](https://raw.githubusercontent.com/qwqcode/Artalk/master/dist/Artalk.js) | [Artalk.css](https://raw.githubusercontent.com/qwqcode/Artalk/master/dist/Artalk.css)
|
||||
> 前端资源下载:[Artalk.js](./dist/Artalk.js) | [Artalk.css](./dist/Artalk.css)
|
||||
|
||||
1. 部署 Artalk 的后端,[PHP 版传送门](https://github.com/qwqcode/Artalk-API-PHP)
|
||||
1. 部署 Artalk 的后端,传送门:[PHP API](https://github.com/qwqcode/Artalk-API-PHP) / Go API / Node API / Python API
|
||||
2. 前端页面配置 Artalk 如下:
|
||||
|
||||
```html
|
||||
@@ -43,19 +43,20 @@
|
||||
<head>
|
||||
<!-- ... -->
|
||||
<link href="dist/Artalk.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="dist/Artalk.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="ArtalkComments"></div>
|
||||
<!-- ... -->
|
||||
<script src="dist/Artalk.js"></script>
|
||||
<script>
|
||||
new Artalk({
|
||||
el: '#ArtalkComments',
|
||||
placeholder: '来啊,快活啊 (/ω\)',
|
||||
noComment: '快来成为第一个评论的人吧~',
|
||||
defaultAvatar: 'mp',
|
||||
el: '#ArtalkComments', // 元素选择
|
||||
placeholder: '来啊,快活啊 ( ゜- ゜)', // 占位符
|
||||
noComment: '快来成为第一个评论的人吧~', // 无评论时显示
|
||||
defaultAvatar: 'mp', // 参考 https://cn.gravatar.com/site/implement/images/#default-image
|
||||
pageKey: '[页面唯一标识]',
|
||||
serverUrl: '[后端程序URL]',
|
||||
readMore: {
|
||||
readMore: { // 阅读更多配置
|
||||
pageSize: 15, // 每次请求获取评论数
|
||||
autoLoad: true // 滚动到底部自动加载
|
||||
}
|
||||
@@ -65,7 +66,9 @@
|
||||
</html>
|
||||
```
|
||||
|
||||
更多配置项详见 [types/artalk-config.d.ts](./types/artalk-config.d.ts)
|
||||
前端更多栗子,请参考 [/example/](./example/) 目录
|
||||
|
||||
前端更多配置项详见 [/types/artalk-config.d.ts](./types/artalk-config.d.ts)
|
||||
|
||||
## 开发
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Artalk v1.0.4
|
||||
* Artalk v1.0.6
|
||||
* (c) 2016-2020 qwqaq.com
|
||||
* Link: https://github.com/qwqcode/Artalk
|
||||
*/.artalk{position:relative;width:100%;min-height:200px}.artalk *,.artalk-layer-wrap *{line-height:1.6em;color:#565a5f;font-family:Helvetica Neue,PingFangSC-Light,Hiragino Sans GB,Microsoft YaHei,WenQuanYi Micro Hei,sans-serif;word-wrap:break-word}.artalk-layer-wrap pre,.artalk pre{line-height:1.6em;background-color:#f6f6f6;color:#555;padding:5px 10px;border-radius:2px;margin:0;font-family:Source Code Pro,courier new,Input Mono,PT Mono,SFMono-Regular,Consolas,Monaco,Menlo,PingFang SC,Liberation Mono,Microsoft YaHei,Courier,monospace}.artalk-layer-wrap a,.artalk a{color:#0083ff;text-decoration:none}.artalk-layer-wrap blockquote,.artalk blockquote{position:static;margin:10px 0;padding:10px 20px;background:#fcfcfc;border-left:4px solid #eee}.artalk-layer-wrap p:first-child,.artalk p:first-child{margin-top:0}.artalk-layer-wrap p:last-child,.artalk p:last-child{margin-bottom:0}.artalk *,.artalk-layer-wrap *{box-sizing:border-box}.artalk-error-layer,.artalk-layer-dialog-wrap,.artalk-loading,.full-layer{width:100%;height:100%;position:absolute;top:0;left:0;background:#fff;z-index:10;align-items:center;justify-content:center;flex-flow:column;display:flex}.artalk-loading{z-index:11}.artalk-loading-spinner{position:relative;width:50px;height:50px}.artalk-loading-spinner svg{-webkit-animation:artalkRotate 2s linear infinite;animation:artalkRotate 2s linear infinite;transform-origin:center center;width:100%;height:100%;position:absolute;top:0;left:0}.artalk-loading-spinner svg circle{stroke-dasharray:1,200;stroke-dashoffset:0;-webkit-animation:artalkDash 1.5s ease-in-out infinite,artalkColor 6s ease-in-out infinite;animation:artalkDash 1.5s ease-in-out infinite,artalkColor 6s ease-in-out infinite;stroke-linecap:round}@-webkit-keyframes artalkRotate{to{transform:rotate(1turn)}}@keyframes artalkRotate{to{transform:rotate(1turn)}}@-webkit-keyframes artalkDash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:89,200;stroke-dashoffset:-35px}to{stroke-dasharray:89,200;stroke-dashoffset:-124px}}@keyframes artalkDash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:89,200;stroke-dashoffset:-35px}to{stroke-dasharray:89,200;stroke-dashoffset:-124px}}@-webkit-keyframes artalkColor{0%,to{stroke:#ff5652}40%{stroke:#2196f3}66%{stroke:#32c787}80%,90%{stroke:#ffc107}}@keyframes artalkColor{0%,to{stroke:#ff5652}40%{stroke:#2196f3}66%{stroke:#32c787}80%,90%{stroke:#ffc107}}@-webkit-keyframes artalkLoadingIconRotate{0%{transform:rotate(0)}50%{transform:rotate(180deg)}to{transform:rotate(1turn)}}@keyframes artalkLoadingIconRotate{0%{transform:rotate(0)}50%{transform:rotate(180deg)}to{transform:rotate(1turn)}}.artalk-loading-icon{border-radius:100%;margin:0;border:1px solid #0083ff;border-bottom-color:transparent;height:15px;width:15px;background:0 0!important;display:inline-block;-webkit-animation:artalkLoadingIconRotate .75s linear 0s infinite;animation:artalkLoadingIconRotate .75s linear 0s infinite;vertical-align:middle}.artalk-fade-in{-webkit-animation:artalkFadeIn .3s both;animation:artalkFadeIn .3s both}.artalk-fade-out{-webkit-animation:artalkFadeOut .2s both;animation:artalkFadeOut .2s both}@-webkit-keyframes artalkFadeIn{0%{opacity:0}to{opacity:1}}@keyframes artalkFadeIn{0%{opacity:0}to{opacity:1}}@-webkit-keyframes artalkFadeOut{to{opacity:0}}@keyframes artalkFadeOut{to{opacity:0}}.artalk-error-layer{background-color:hsla(0,0%,100%,.94)}.artalk-error-layer .artalk-error-title{color:#ff5652}.artalk-error-layer .artalk-error-text{text-align:center}.artalk-layer-dialog-wrap{background-color:hsla(0,0%,100%,.94)}.artalk-layer-dialog-wrap>.artalk-layer-dialog{width:30%}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-content .artalk-captcha-img{cursor:pointer;width:100px;height:auto;margin-right:10px;padding-right:10px;border-right:1px solid #eee;vertical-align:bottom}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-content input{width:100%;line-height:29px;border:1px solid #999;border-radius:2px;outline:none;padding:0 6px;display:block;margin-top:10px;margin-bottom:5px;text-align:center}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action{display:flex;flex-direction:row}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action button{flex:1;display:block;cursor:pointer;border:1px solid #0083ff;background:transparent;color:#0083ff;border-radius:2px;padding:0 15px;line-height:25px;outline:none}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action button:active{color:#fff;background:#0083ff}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action button:not(:last-child){margin-right:5px}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action button.error{color:#fff;background:#ff5652;border-color:#ff5652}@media only screen and (max-width:768px){.artalk-layer-dialog-wrap>.artalk-layer-dialog{width:90%!important}}.artalk-notify{display:block;overflow:hidden;background-cLine truncated
|
||||
*/.artalk{position:relative;width:100%;min-height:200px}.artalk *,.artalk-layer-wrap *{line-height:1.6em;color:#565a5f;font-family:Helvetica Neue,PingFangSC-Light,Hiragino Sans GB,Microsoft YaHei,WenQuanYi Micro Hei,sans-serif;word-wrap:break-word;word-break:break-all}.artalk-layer-wrap pre,.artalk pre{line-height:1.6em;background-color:#f6f6f6;color:#555;padding:5px 10px;border-radius:2px;margin:0;font-family:Source Code Pro,courier new,Input Mono,PT Mono,SFMono-Regular,Consolas,Monaco,Menlo,PingFang SC,Liberation Mono,Microsoft YaHei,Courier,monospace}.artalk-layer-wrap a,.artalk a{color:#0083ff;text-decoration:none}.artalk-layer-wrap blockquote,.artalk blockquote{position:static;margin:10px 0;padding:10px 20px;background:#fcfcfc;border-left:4px solid #eee}.artalk-layer-wrap p:first-child,.artalk p:first-child{margin-top:0}.artalk-layer-wrap p:last-child,.artalk p:last-child{margin-bottom:0}.artalk *,.artalk-layer-wrap *{box-sizing:border-box}.artalk-layer-wrap img,.artalk img{max-width:100%}.artalk-error-layer,.artalk-layer-dialog-wrap,.artalk-loading,.full-layer{width:100%;height:100%;position:absolute;top:0;left:0;background:#fff;z-index:10;align-items:center;justify-content:center;flex-flow:column;display:flex}.artalk-loading{z-index:11}.artalk-loading-spinner{position:relative;width:50px;height:50px}.artalk-loading-spinner svg{-webkit-animation:artalkRotate 2s linear infinite;animation:artalkRotate 2s linear infinite;transform-origin:center center;width:100%;height:100%;position:absolute;top:0;left:0}.artalk-loading-spinner svg circle{stroke-dasharray:1,200;stroke-dashoffset:0;-webkit-animation:artalkDash 1.5s ease-in-out infinite,artalkColor 6s ease-in-out infinite;animation:artalkDash 1.5s ease-in-out infinite,artalkColor 6s ease-in-out infinite;stroke-linecap:round}@-webkit-keyframes artalkRotate{to{transform:rotate(1turn)}}@keyframes artalkRotate{to{transform:rotate(1turn)}}@-webkit-keyframes artalkDash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:89,200;stroke-dashoffset:-35px}to{stroke-dasharray:89,200;stroke-dashoffset:-124px}}@keyframes artalkDash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:89,200;stroke-dashoffset:-35px}to{stroke-dasharray:89,200;stroke-dashoffset:-124px}}@-webkit-keyframes artalkColor{0%,to{stroke:#ff5652}40%{stroke:#2196f3}66%{stroke:#32c787}80%,90%{stroke:#ffc107}}@keyframes artalkColor{0%,to{stroke:#ff5652}40%{stroke:#2196f3}66%{stroke:#32c787}80%,90%{stroke:#ffc107}}@-webkit-keyframes artalkLoadingIconRotate{0%{transform:rotate(0)}to{transform:rotate(1turn)}}@keyframes artalkLoadingIconRotate{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.artalk-loading-icon{width:18px;height:18px;box-sizing:border-box;border-color:#29d transparent transparent #29d;border-style:solid;border-width:1px;border-radius:50%;-webkit-animation:artalkLoadingIconRotate .4s linear infinite;animation:artalkLoadingIconRotate .4s linear infinite}.artalk-fade-in{-webkit-animation:artalkFadeIn .3s both;animation:artalkFadeIn .3s both}.artalk-fade-out{-webkit-animation:artalkFadeOut .2s both;animation:artalkFadeOut .2s both}@-webkit-keyframes artalkFadeIn{0%{opacity:0}to{opacity:1}}@keyframes artalkFadeIn{0%{opacity:0}to{opacity:1}}@-webkit-keyframes artalkFadeOut{to{opacity:0}}@keyframes artalkFadeOut{to{opacity:0}}.artalk-error-layer{background-color:hsla(0,0%,100%,.94)}.artalk-error-layer .artalk-error-title{color:#ff5652}.artalk-error-layer .artalk-error-text{text-align:center}.artalk-layer-dialog-wrap{background-color:hsla(0,0%,100%,.94)}.artalk-layer-dialog-wrap>.artalk-layer-dialog{width:25%}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-content .artalk-captcha-img{cursor:pointer;width:100px;height:auto;margin-right:10px;padding-right:10px;border-right:1px solid #eee;vertical-align:bottom}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-content input{width:100%;line-height:34px;border:1px solid #999;border-radius:2px;outline:none;padding:0 6px;display:block;margin-top:10px;margin-bottom:5px;text-align:center}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action{display:flex;flex-direction:row}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action button{flex:1;display:block;cursor:pointer;border:1px solid #0083ff;background:transparent;color:#0083ff;border-radius:2px;padding:0 15px;line-height:30px;outline:none}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action button:active{color:#fff;background:#0083ff}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action button:not(:last-child){margin-right:5px}.artalk-layer-dialog-wrap>.artalk-layer-dialog>.artalk-layer-dialog-action button.error{color:#fff;background:#ff5652;border-color:#ff5652}@media only screen and (max-width:768px){.artalk-layer-dialog-wrap>.artalk-layer-dialog{width:90%!important}}.artalk-notify{display:block;overflow:hidden;background-color:#2c2c2c;color:#fff;bLine truncated
|
||||
Vendored
+3
-3
@@ -1,13 +1,13 @@
|
||||
/*!
|
||||
* Artalk v1.0.4
|
||||
* Artalk v1.0.6
|
||||
* (c) 2016-2020 qwqaq.com
|
||||
* Link: https://github.com/qwqcode/Artalk
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Artalk",[],t):"object"==typeof exports?exports.Artalk=t():e.Artalk=t()}(window,function(){return d=[function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t){e.exports=function(e){return e&&e.__esModule?e:{default:e}}},function(e,t){function r(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}e.exports=function(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}},function(e,t,n){var r=n(20);e.exports=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&r(e,t)}},function(e,t,n){var r=n(11),a=n(8);e.exports=function(e,t){return!t||"object"!==r(t)&&"function"!=typeof t?a(e):t}},function(t,e){function n(e){return t.exports=n=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},n(e)}t.exports=n},function(i,l,s){var e,t,n;t=[l,s(0),s(9)],void 0===(n="function"==typeof(e=function(e,t,n){"use strict";var r=s(1);Object.defineProperty(e,"__esModule",{value:true}),e["default"]=void 0,t=r(t);var a=function e(){(0,t["default"])(this,e);this.artalk=n.ArtalkInstance};(e["default"]=a).displayName="ArtalkContext",i.exports=l.default})?e.apply(l,t):e)||(i.exports=n)},function(i,l,s){var e,t,n;t=[l,s(0),s(2)],void 0===(n="function"==typeof(e=function(e,t,n){"use strict";var r=s(1);Object.defineProperty(e,"__esModule",{value:true}),e["default"]=void 0,t=r(t),n=r(n);var a=function(){function e(){(0,t["default"])(this,e)}(0,n["default"])(e,null,[{key:"createElement",value:function e(){var t=arguments.length>0&&arguments[0]!==undefined?arguments[0]:"";var n=document.createElement("div");n.innerHTML=t.trim();return n.firstElementChild||n}},{key:"getHeight",value:function e(t){return parseFloat(getComputedStyle(t,null).height.replace("px",""))}},{key:"htmlEncode",value:function e(t){var n=document.createElement("div");n.innerText=t;var r=n.innerHTML;n=null;return r}},{key:"htmlDecode",value:function e(t){var n=document.createElement("div");n.innerHTML=t;var r=n.innerText;n=null;return r}},{key:"getLocationParmByName",value:function e(t){var n=RegExp("[?&]".concat(t,"=([^&]*)")).exec(window.location.search);return n&&decodeURIComponent(n[1].replace(/\+/g," "))}},{key:"getOffset",value:function e(t){var n=t.getBoundingClientRect();return{top:n.top+window.scrollY,left:n.left+window.scrollX}}}]);return e}();(e["default"]=a).displayName="Utils",i.exports=l.default})?e.apply(l,t):e)||(i.exports=n)},function(e,t){e.exports=function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}},function(e,t,h){var n,r,a;r=[t,h(0),h(2),h(15),h(16),h(18),h(19),h(32),h(38),h(41)],void 0===(a="function"==typeof(n=function(n,r,e,t,l,s,a,i,o,c){"use strict";var u=h(1);Object.defineProperty(n,"__esModule",{value:true}),n["default"]=n.ArtalkInstance=void 0,r=u(r),e=u(e),l=u(l),s=u(s),a=u(a),i=u(i),o=u(o),c=u(c);var d={el:"",placeholder:"来啊,快活啊 (/ω\)",noComment:"快来成为第一个评论的人吧~",defaultAvatar:"mp",pageKey:"",serverUrl:"",emoticons:h(42),gravatar:{cdn:"https://gravatar.loli.net/avatar/"}},f=null;n.ArtalkInstance=f;var p=function(){function t(e){(0,r["default"])(this,t);this.conf=e;console.log("\n %c "+"Artalk v".concat("1.0.4"," %c 一款简洁有趣的自托管评论系统 \n\n%c")+"> https://artalk.js.org\n"+"> https://github.com/qwqcode/Artalk\n"+"> https://qwqaq.com\n","color: #FFF; background: #1DAAFF; padding:5px 0;","color: #FFF; background: #656565; padding:5px 0;","");n.ArtalkInstance=f=this;this.conf=Object.assign(d,this.conf);try{this.el=document.querySelector(this.conf.el);if(this.el===null){throw Error('Sorry, Target element "'.concat(this.conf.el,'" was not found.'))}}catch(e){console.error(e);throw new Error("Artalk config `el` error")}this.el.classList.add("artalk");this.ui=new c["default"];this.initUser();this.initMarked();this.editor=new a["default"];this.list=new i["default"];this.sidebar=new o["default"]}(0,e["default"])(t,[{key:"initUser",value:function e(){var t=JSON.parse(window.localStorage.getItem("ArtalkUser")||"{}");this.user={nick:t.nick||"",email:t.email||"",link:t.link||"",password:t.password||"",isAdmin:t.isAdmin||false}}},{key:"saveUser",value:function e(){window.localStorage.setItem("ArtalkUser",JSON.stringify(this.user))}},{key:"checkHasBasicUserInfo",value:function e(){return!!this.user.nick&&!!this.user.email}},{key:"initMarked",value:function e(){var a=new l["default"].Renderer;var i=a.link;a.link=function(e,t,n){var r=i.call(a,e,t,n);return r.replace(/^<a /,'<a target="_blank" rel="nofollow" ')};var t=l["default"];tLine truncated
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Artalk",[],t):"object"==typeof exports?exports.Artalk=t():e.Artalk=t()}(this,function(){return d=[function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t){e.exports=function(e){return e&&e.__esModule?e:{default:e}}},function(e,t){function a(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}e.exports=function(e,t,n){return t&&a(e.prototype,t),n&&a(e,n),e}},function(e,t,n){var a=n(20);e.exports=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&a(e,t)}},function(e,t,n){var a=n(10),r=n(8);e.exports=function(e,t){return!t||"object"!==a(t)&&"function"!=typeof t?r(e):t}},function(t,e){function n(e){return t.exports=n=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},n(e)}t.exports=n},function(r,i,l){var e,t,n;t=[i,l(0)],void 0===(n="function"==typeof(e=function(e,n){"use strict";var t=l(1);Object.defineProperty(e,"__esModule",{value:true}),e["default"]=void 0,n=t(n);var a=function e(t){(0,n["default"])(this,e);this.artalk=t};(e["default"]=a).displayName="ArtalkContext",r.exports=i.default})?e.apply(i,t):e)||(r.exports=n)},function(i,l,s){var e,t,n;t=[l,s(0),s(2)],void 0===(n="function"==typeof(e=function(e,t,n){"use strict";var a=s(1);Object.defineProperty(e,"__esModule",{value:true}),e["default"]=void 0,t=a(t),n=a(n);var r=function(){function e(){(0,t["default"])(this,e)}(0,n["default"])(e,null,[{key:"createElement",value:function e(){var t=arguments.length>0&&arguments[0]!==undefined?arguments[0]:"";var n=document.createElement("div");n.innerHTML=t.trim();return n.firstElementChild||n}},{key:"getHeight",value:function e(t){return parseFloat(getComputedStyle(t,null).height.replace("px",""))}},{key:"htmlEncode",value:function e(t){var n=document.createElement("div");n.innerText=t;var a=n.innerHTML;n=null;return a}},{key:"htmlDecode",value:function e(t){var n=document.createElement("div");n.innerHTML=t;var a=n.innerText;n=null;return a}},{key:"getLocationParmByName",value:function e(t){var n=RegExp("[?&]".concat(t,"=([^&]*)")).exec(window.location.search);return n&&decodeURIComponent(n[1].replace(/\+/g," "))}},{key:"getOffset",value:function e(t){var n=t.getBoundingClientRect();return{top:n.top+window.scrollY,left:n.left+window.scrollX}}},{key:"timeAgo",value:function e(t){try{var n=t.getTime();var a=(new Date).getTime();var r=a-n;var i=Math.floor(r/(24*3600*1e3));if(i===0){var l=r%(24*3600*1e3);var s=Math.floor(l/(3600*1e3));if(s===0){var o=l%(3600*1e3);var c=Math.floor(o/(60*1e3));if(c===0){var u=o%(60*1e3);var d=Math.round(u/1e3);return"".concat(d," 秒前")}return"".concat(c," 分钟前")}return"".concat(s," 小时前")}if(i<0)return"刚刚";if(i<8){return"".concat(i," 天前")}return this.dateFormat(t)}catch(e){console.error(e);return" - "}}},{key:"padWithZeros",value:function e(t,n){var a=t.toString();while(a.length<n){a="0".concat(a)}return a}},{key:"dateFormat",value:function e(t){var n=this.padWithZeros(t.getDate(),2);var a=this.padWithZeros(t.getMonth()+1,2);var r=this.padWithZeros(t.getFullYear(),2);return"".concat(r,"-").concat(a,"-").concat(n)}}]);return e}();(e["default"]=r).displayName="Utils",i.exports=l.default})?e.apply(l,t):e)||(i.exports=n)},function(e,t){e.exports=function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}},function(h,m,g){var e,t,n;t=[m,g(0),g(2),g(3),g(4),g(5),g(24),g(25),g(6),g(7)],void 0===(n="function"==typeof(e=function(e,l,t,n,r,i,a,s,o,c){"use strict";var u=g(1);function d(a){return function(){var e=(0,i["default"])(a),t;if(f()){var n=(0,i["default"])(this).constructor;t=Reflect.construct(e,arguments,n)}else{t=e.apply(this,arguments)}return(0,r["default"])(this,t)}}function f(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return true}catch(e){return false}}Object.defineProperty(e,"__esModule",{value:true}),e["default"]=void 0,l=u(l),t=u(t),n=u(n),r=u(r),i=u(i),s=u(s),o=u(o),c=u(c);var p=function(e){(0,n["default"])(i,e);var r=d(i);function i(e,t,n){var a;(0,l["default"])(this,i);a=r.call(this,e);a.list=t;a.data=n;a.maxNestedNo=3;a.children=[];a.data=Object.assign({},a.data);a.parent=null;a.nestedNum=1;a.renderElem();return a}(0,t["default"])(i,[{key:"renderElem",value:function e(){var t=this;this.elem=c["default"].createElement(g(26)(this));this.mainEl=this.elem.querySelector(".artalk-comment-main");this.bodyEl=this.Line truncated
|
||||
/*!
|
||||
* Determine if an object is a Buffer
|
||||
*
|
||||
* @author Feross Aboukhadijeh <https://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
e.exports=function(e){return null!=e&&(n(e)||"function"==typeof(t=e).readFloatLE&&"function"==typeof t.slice&&n(t.slice(0,0))||!!e._isBuffer);var t}},function(e,t,n){},function(module,exports){module.exports=function anonymous(locals,filters,escape,rethrow){escape=escape||function(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/'/g,"'").replace(/"/g,""")};var __stack={lineno:1,input:'<div class="artalk-list">\n <div class="artalk-list-header">\n <div class="artalk-comment-count">\n <span class="artalk-comment-count-num">0</span>\n 条评论\n </div>\n <div class="artalk-right-action">\n <span data-action="open-sidebar" style="display: none;">通知中心</span>\n </div>\n </div>\n <div class="artalk-list-body">\n <div class="artalk-list-comments-wrap">\n </div>\n <div class="artalk-list-read-more" style="display: none;">\n <div class="artalk-loading-icon" style="display: none;"></div>\n <span class="artalk-text">查看更多</span>\n </div>\n </div>\n <div class="artalk-list-footer">\n <div class="artalk-copyright">\n Powered By <a href="https://artalk.js.org" target="_blank" title="qwqcode/Artalk v<%= ARTALK_VERSION %>">Artalk</a>\n </div>\n </div>\n</div>\n',filename:"."};function rethrow(e,t,n,r){var a=t.split("\n"),i=Math.max(r-3,0),l=Math.min(a.length,r+3),s=a.slice(i,l).map(function(e,t){var n=t+i+1;return(n==r?" >> ":" ")+n+"| "+e}).join("\n");throw e.path=n,e.message=(n||"ejs")+":"+r+"\n"+s+"\n\n"+e.message,e}try{var buf=[];with(locals||{})buf.push('<div class="artalk-list">\n <div class="artalk-list-header">\n <div class="artalk-comment-count">\n <span class="artalk-comment-count-num">0</span>\n 条评论\n </div>\n <div class="artalk-right-action">\n <span data-action="open-sidebar" style="display: none;">通知中心</span>\n </div>\n </div>\n <div class="artalk-list-body">\n <div class="artalk-list-comments-wrap">\n </div>\n <div class="artalk-list-read-more" style="display: none;">\n <div class="artalk-loading-icon" style="display: none;"></div>\n <span class="artalk-text">查看更多</span>\n </div>\n </div>\n <div class="artalk-list-footer">\n <div class="artalk-copyright">\n Powered By <a href="https://artalk.js.org" target="_blank" title="qwqcode/Artalk v',escape((__stack.lineno=21,"1.0.4")),'">Artalk</a>\n </div>\n </div>\n</div>\n');return buf.join("")}catch(e){rethrow(e,__stack.input,__stack.filename,__stack.lineno)}}},function(g,v,k){var e,t,n;t=[v,k(0),k(2),k(8),k(3),k(4),k(5),k(10),k(39),k(6),k(7),k(12)],void 0===(n="function"==typeof(e=function(e,r,a,i,l,s,o,c,t,n,u,d){"use strict";var f=k(1);function p(r){return function(){var e=(0,o["default"])(r),t;if(h()){var n=(0,o["default"])(this).constructor;t=Reflect.construct(e,arguments,n)}else{t=e.apply(this,arguments)}return(0,s["default"])(this,t)}}function h(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return true}catch(e){return false}}Object.defineProperty(e,"__esModule",{value:true}),e["default"]=void 0,r=f(r),a=f(a),i=f(i),l=f(l),s=f(s),o=f(o),c=f(c),n=f(n),u=f(u),d=f(d);var m=function(e){(0,l["default"])(n,e);var t=p(n);function n(){var e;(0,r["default"])(this,n);e=t.call(this);e.el=u["default"].createElement(k(40)((0,i["default"])(e)));e.layer=new d["default"]("sidebar",e.el);e.contentEl=e.el.querySelector(".artalk-sidebar-content");e.el.querySelector(".artalk-sidebar-close").addEventListener("click",function(){e.hide()});return e}(0,a["default"])(n,[{key:"show",value:function e(){var n=this;this.layer.show();this.contentEl.scrollTo(0,0);setTimeout(function(){n.el.style.transform="translate(0, 0)"},20);this.artalk.request("CommentReplyGet",{nick:this.artalk.user.nick,email:this.artalk.user.email},function(){n.artalk.ui.showLoading(n.contentEl)},function(){n.artalk.ui.hideLoading(n.contentEl)},function(e,t){n.contentEl.innerHTML="";if(Array.isArray(t.reply_comments)){t.reply_comments.forEach(function(e){n.putComment(e)})}},function(e,t){})}},{key:"hide",value:function e(){this.el.style.transform="";this.layer.hide()}},{key:"putComment",value:function e(t){var n=new c["default"](null,t);n.elem.querySelector('[data-comment-action="reply"]').remove();n.elem.style.cursor="pointer";n.elem.addEventListener("mouseover",function(){n.elem.style.backgroundColor="#F4F4F4"});n.elem.addEventListener("mouseout",function(){n.elem.style.backgroundColor=""});n.elem.addEventListener("click",function(e){e.preventDefault();window.location.href="".concat(t.page_key,"#artalk-comment-").concat(n.data.id)});this.contentEl.appendChild(n.getElem())}}]);return n}(n["default"]);(e["default"]=m).displayName="Sidebar",g.exports=v.default})?e.apply(v,t):e)||(g.exports=n)},function(e,t,n){},function(moLine truncated
|
||||
e.exports=function(e){return null!=e&&(n(e)||"function"==typeof(t=e).readFloatLE&&"function"==typeof t.slice&&n(t.slice(0,0))||!!e._isBuffer);var t}},function(e,t,n){},function(module,exports){module.exports=function anonymous(locals,filters,escape,rethrow){escape=escape||function(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/'/g,"'").replace(/"/g,""")};var __stack={lineno:1,input:'<div class="artalk-list">\n <div class="artalk-list-header">\n <div class="artalk-comment-count">\n <span class="artalk-comment-count-num">0</span>\n 条评论\n </div>\n <div class="artalk-right-action">\n <span data-action="admin-close-comment" class="artalk-hide" data-list-ui-only-admin>关闭评论</span>\n <span data-action="open-sidebar" class="artalk-hide artalk-on">通知中心</span>\n </div>\n </div>\n <div class="artalk-list-body">\n <div class="artalk-list-comments-wrap">\n </div>\n <div class="artalk-list-read-more" style="display: none;">\n <div class="artalk-loading-icon" style="display: none;"></div>\n <span class="artalk-text">查看更多</span>\n </div>\n </div>\n <div class="artalk-list-footer">\n <div class="artalk-copyright">\n Powered By <a href="https://artalk.js.org" target="_blank" title="qwqcode/Artalk v<%= ARTALK_VERSION %>">Artalk</a>\n </div>\n </div>\n</div>\n',filename:"."};function rethrow(e,t,n,a){var r=t.split("\n"),i=Math.max(a-3,0),l=Math.min(r.length,a+3),s=r.slice(i,l).map(function(e,t){var n=t+i+1;return(n==a?" >> ":" ")+n+"| "+e}).join("\n");throw e.path=n,e.message=(n||"ejs")+":"+a+"\n"+s+"\n\n"+e.message,e}try{var buf=[];with(locals||{})buf.push('<div class="artalk-list">\n <div class="artalk-list-header">\n <div class="artalk-comment-count">\n <span class="artalk-comment-count-num">0</span>\n 条评论\n </div>\n <div class="artalk-right-action">\n <span data-action="admin-close-comment" class="artalk-hide" data-list-ui-only-admin>关闭评论</span>\n <span data-action="open-sidebar" class="artalk-hide artalk-on">通知中心</span>\n </div>\n </div>\n <div class="artalk-list-body">\n <div class="artalk-list-comments-wrap">\n </div>\n <div class="artalk-list-read-more" style="display: none;">\n <div class="artalk-loading-icon" style="display: none;"></div>\n <span class="artalk-text">查看更多</span>\n </div>\n </div>\n <div class="artalk-list-footer">\n <div class="artalk-copyright">\n Powered By <a href="https://artalk.js.org" target="_blank" title="qwqcode/Artalk v',escape((__stack.lineno=22,"1.0.6")),'">Artalk</a>\n </div>\n </div>\n</div>\n');return buf.join("")}catch(e){rethrow(e,__stack.input,__stack.filename,__stack.lineno)}}},function(g,k,v){var e,t,n;t=[k,v(0),v(2),v(8),v(3),v(4),v(5),v(9),v(46),v(6),v(7),v(11)],void 0===(n="function"==typeof(e=function(e,r,t,i,l,s,o,c,n,a,u,d){"use strict";var f=v(1);function p(a){return function(){var e=(0,o["default"])(a),t;if(h()){var n=(0,o["default"])(this).constructor;t=Reflect.construct(e,arguments,n)}else{t=e.apply(this,arguments)}return(0,s["default"])(this,t)}}function h(){if(typeof Reflect==="undefined"||!Reflect.construct)return false;if(Reflect.construct.sham)return false;if(typeof Proxy==="function")return true;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return true}catch(e){return false}}Object.defineProperty(e,"__esModule",{value:true}),e["default"]=void 0,r=f(r),t=f(t),i=f(i),l=f(l),s=f(s),o=f(o),c=f(c),a=f(a),u=f(u),d=f(d);var m=function(e){(0,l["default"])(a,e);var n=p(a);function a(e){var t;(0,r["default"])(this,a);t=n.call(this,e);t.el=u["default"].createElement(v(47)((0,i["default"])(t)));t.contentEl=t.el.querySelector(".artalk-sidebar-content");t.el.querySelector(".artalk-sidebar-close").addEventListener("click",function(){t.hide()});return t}(0,t["default"])(a,[{key:"show",value:function e(){var n=this;this.layer=(0,d["default"])(this.artalk,"sidebar",this.el);this.layer.show();this.contentEl.scrollTo(0,0);setTimeout(function(){n.el.style.transform="translate(0, 0)"},20);this.artalk.request("CommentReplyGet",{nick:this.artalk.user.data.nick,email:this.artalk.user.data.email},function(){n.artalk.ui.showLoading(n.contentEl)},function(){n.artalk.ui.hideLoading(n.contentEl)},function(e,t){n.contentEl.innerHTML="";if(Array.isArray(t.reply_comments)){t.reply_comments.forEach(function(e){n.putComment(e)})}},function(e,t){})}},{key:"hide",value:function e(){this.el.style.transform="";this.layer.dispose()}},{key:"putComment",value:function e(t){var n=new c["default"](this.artalk,null,t);n.elem.querySelector('[data-comment-action="reply"]').remove();n.elem.style.cursor="pointer";n.elem.addEventListener("mouseover",function(){n.elem.style.backgroundColor="#F4F4F4"});n.elem.addEventListener("mouseout",function(){n.elem.style.backgroundColor=""});n.elem.addEventListener("click",function(e){e.preventDLine truncated
|
||||
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Multiple Artalk On One Page Example</title>
|
||||
<!-- 1. 引入 Artalk.css -->
|
||||
<link rel="stylesheet" href="/dist/Artalk.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Multiple Artalk On One Page Example</h1>
|
||||
|
||||
<div class="main">
|
||||
<h2>Artalk One</h2>
|
||||
<div id="ArtalkComments-1"></div>
|
||||
|
||||
<h2>Artalk Two</h2>
|
||||
<div id="ArtalkComments-2"></div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 引入 Artalk.js -->
|
||||
<script src="/dist/Artalk.js"></script>
|
||||
<!-- 3. 配置 Artalk -->
|
||||
<script>
|
||||
(function () {
|
||||
// 公用配置
|
||||
const commonConf = {
|
||||
placeholder: '来啊,快活啊 ( ゜- ゜)',
|
||||
noComment: '快来成为第一个评论的人吧~',
|
||||
defaultAvatar: 'mp',
|
||||
serverUrl: 'http://localhost:23366/',
|
||||
readMore: {
|
||||
pageSize: 15,
|
||||
autoLoad: true
|
||||
}
|
||||
}
|
||||
|
||||
// 第一个评论
|
||||
new Artalk({
|
||||
el: '#ArtalkComments-1',
|
||||
pageKey: 'multiple-on-one-page-1',
|
||||
...commonConf
|
||||
})
|
||||
|
||||
// 第二个评论
|
||||
new Artalk({
|
||||
el: '#ArtalkComments-2',
|
||||
pageKey: 'multiple-on-one-page-2',
|
||||
...commonConf
|
||||
})
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Artalk Normal Example</title>
|
||||
<!-- 1. 引入 Artalk.css -->
|
||||
<link rel="stylesheet" href="/dist/Artalk.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Artalk Normal Example</h1>
|
||||
|
||||
<div class="main">
|
||||
<div id="ArtalkComments"></div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 引入 Artalk.js -->
|
||||
<script src="/dist/Artalk.js"></script>
|
||||
<!-- 3. 配置 Artalk -->
|
||||
<script>
|
||||
var artalk = new Artalk({
|
||||
el: '#ArtalkComments', // 元素选择
|
||||
placeholder: '来啊,快活啊 ( ゜- ゜)', // 占位符
|
||||
noComment: '快来成为第一个评论的人吧~', // 无评论时显示
|
||||
defaultAvatar: 'mp', // 参考 https://cn.gravatar.com/site/implement/images/#default-image
|
||||
pageKey: 'Artalk Normal Example',
|
||||
serverUrl: 'http://localhost:23366/',
|
||||
readMore: { // 阅读更多配置
|
||||
pageSize: 15, // 每次请求获取评论数
|
||||
autoLoad: true // 滚动到底部自动加载
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Artalk Pjax Example</title>
|
||||
<!-- 1. 引入 Artalk.css -->
|
||||
<link rel="stylesheet" href="/dist/Artalk.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Artalk Pjax Example</h1>
|
||||
<div>
|
||||
Go to <a href="./res1.html">第一页</a> | <a href="./res2.html">第二页</a>
|
||||
</div>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
|
||||
<!-- 2. 引入 Artalk.js -->
|
||||
<script src="/dist/Artalk.js"></script>
|
||||
|
||||
<!-- Pjax 配置 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery-pjax@2.0.1/jquery.pjax.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).pjax('a', '#container', {
|
||||
fragment: '.main' // △ 提取 AJAX 响应内容中指定的元素内容片段;参考:http://bsify.admui.com/jquery-pjax/
|
||||
})
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Res</title>
|
||||
<!-- 1. 引入 Artalk.css -->
|
||||
<link rel="stylesheet" href="/dist/Artalk.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="main"> <!-- △ fragment: .main -->
|
||||
<section id="comments">
|
||||
<div id="ArtalkComments"></div>
|
||||
</section>
|
||||
|
||||
<!-- 2. 引入 Artalk.js -->
|
||||
<script src="/dist/Artalk.js"></script>
|
||||
<!-- 3. 配置 Artalk -->
|
||||
<script>
|
||||
window.artalk = new Artalk({
|
||||
el: '#ArtalkComments',
|
||||
placeholder: '来啊,快活啊 ( ゜- ゜)',
|
||||
noComment: '快来成为第一个评论的人吧~',
|
||||
defaultAvatar: 'mp',
|
||||
pageKey: 'pjax-example-page1',
|
||||
serverUrl: 'http://localhost:23366/index.php',
|
||||
readMore: {
|
||||
pageSize: 15,
|
||||
autoLoad: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Pjax 配置 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery-pjax@2.0.1/jquery.pjax.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Res</title>
|
||||
<!-- 1. 引入 Artalk.css -->
|
||||
<link rel="stylesheet" href="/dist/Artalk.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="main"> <!-- △ fragment: .main -->
|
||||
<section id="comments">
|
||||
<div id="ArtalkComments"></div>
|
||||
</section>
|
||||
|
||||
<!-- 2. 引入 Artalk.js -->
|
||||
<script src="/dist/Artalk.js"></script>
|
||||
<!-- 3. 配置 Artalk -->
|
||||
<script>
|
||||
window.artalk = new Artalk({
|
||||
el: '#ArtalkComments',
|
||||
placeholder: '来啊,快活啊 ( ゜- ゜)',
|
||||
noComment: '快来成为第一个评论的人吧~',
|
||||
defaultAvatar: 'mp',
|
||||
pageKey: 'pjax-example-page2',
|
||||
serverUrl: 'http://localhost:23366/index.php',
|
||||
readMore: {
|
||||
pageSize: 15,
|
||||
autoLoad: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Pjax 配置 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery-pjax@2.0.1/jquery.pjax.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+36
-3
@@ -5,6 +5,7 @@
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body { padding:45px 10px;margin:0;background:#f4f4f4 }
|
||||
.card { margin:0 auto;max-width:700px;position:relative;overflow:hidden;background:#FFF;border-radius:4px;padding:20px }
|
||||
.card:not(:last-child) { margin-bottom:10px }
|
||||
@@ -16,10 +17,16 @@
|
||||
.desc { font-size:12px;color:#757e91 }
|
||||
.social { margin-top:20px }
|
||||
.social > a:not(:last-child) { margin-right:2px }
|
||||
@media only screen and (max-width: 768px) {
|
||||
body { padding: 0;background: #FFF; }
|
||||
.card.billboard { padding-top: 20px; }
|
||||
.card { width: 100%;padding: 5px; }
|
||||
}
|
||||
.to-top { right: 25px;bottom: 25px;display: block;cursor: pointer;text-align: center;width: 60px;height: 37px;line-height: 37px;font-size: 13px;color: #757e91;background: #fff;opacity: 0.8;border-radius: 4px;border: 1px solid #eceff2;position: fixed; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card billboard">
|
||||
<div class="card billboard artalk-fade-in">
|
||||
<div class="title"><a href="https://github.com/qwqcode/Artalk" target="_blank"><span>> </span>Artalk<span> <</span></a></div>
|
||||
<div class="desc">一款简洁有趣的自托管评论系统</div>
|
||||
<div class="social">
|
||||
@@ -31,13 +38,14 @@
|
||||
<a href="https://github.com/qwqcode/Artalk" target="_blank"><img src="https://img.shields.io/github/stars/qwqcode/Artalk.svg?style=social"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card artalk-fade-in">
|
||||
<div id="comments"></div>
|
||||
</div>
|
||||
<div class="to-top artalk-fade-in" style="display: none;">TOP</div>
|
||||
<script>
|
||||
var artalk = new Artalk({
|
||||
el: '#comments',
|
||||
placeholder: '来啊,快活啊 (/ω\)',
|
||||
placeholder: '来啊,快活啊 ( ゜- ゜)',
|
||||
defaultAvatar: 'mp',
|
||||
pageKey: 'https://artalk.js.org/',
|
||||
serverUrl: (location.hostname === 'localhost' || location.hostname === '127.0.0.1') ? 'http://localhost:23366/' : 'https://qwqaq.com/Artalk/index.php',
|
||||
@@ -47,5 +55,30 @@
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<script>
|
||||
(function () {
|
||||
const toTopElem = document.querySelector('.to-top')
|
||||
toTopElem.addEventListener('click', () => {
|
||||
window.scrollTo(0, 0)
|
||||
})
|
||||
|
||||
checkShowToTop()
|
||||
document.addEventListener('scroll', () => {
|
||||
checkShowToTop()
|
||||
})
|
||||
|
||||
function checkShowToTop() {
|
||||
const scrollTop = window.scrollY
|
||||
const whereShow = window.screen.height
|
||||
const isNeedShow = (scrollTop > 0) && (scrollTop > whereShow)
|
||||
|
||||
if (isNeedShow) {
|
||||
toTopElem.style.display = ''
|
||||
} else {
|
||||
toTopElem.style.display = 'none'
|
||||
}
|
||||
}
|
||||
})()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+37
-4
@@ -5,6 +5,7 @@
|
||||
<title>Artalk DEMO</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body { padding:45px 10px;margin:0;background:#f4f4f4 }
|
||||
.card { margin:0 auto;max-width:700px;position:relative;overflow:hidden;background:#FFF;border-radius:4px;padding:20px }
|
||||
.card:not(:last-child) { margin-bottom:10px }
|
||||
@@ -16,10 +17,16 @@
|
||||
.desc { font-size:12px;color:#757e91 }
|
||||
.social { margin-top:20px }
|
||||
.social > a:not(:last-child) { margin-right:2px }
|
||||
@media only screen and (max-width: 768px) {
|
||||
body { padding: 0;background: #FFF; }
|
||||
.card.billboard { padding-top: 20px; }
|
||||
.card { width: 100%;padding: 5px; }
|
||||
}
|
||||
.to-top { right: 25px;bottom: 25px;display: block;cursor: pointer;text-align: center;width: 60px;height: 37px;line-height: 37px;font-size: 13px;color: #757e91;background: #fff;opacity: 0.8;border-radius: 4px;border: 1px solid #eceff2;position: fixed; }
|
||||
</style>
|
||||
<link href="dist/Artalk.css?864ece85c9b2d3868105" rel="stylesheet"><script type="text/javascript" src="dist/Artalk.js?864ece85c9b2d3868105"></script></head>
|
||||
<link href="dist/Artalk.css?32605c1999ff344cda74" rel="stylesheet"><script type="text/javascript" src="dist/Artalk.js?32605c1999ff344cda74"></script></head>
|
||||
<body>
|
||||
<div class="card billboard">
|
||||
<div class="card billboard artalk-fade-in">
|
||||
<div class="title"><a href="https://github.com/qwqcode/Artalk" target="_blank"><span>> </span>Artalk<span> <</span></a></div>
|
||||
<div class="desc">一款简洁有趣的自托管评论系统</div>
|
||||
<div class="social">
|
||||
@@ -31,13 +38,14 @@
|
||||
<a href="https://github.com/qwqcode/Artalk" target="_blank"><img src="https://img.shields.io/github/stars/qwqcode/Artalk.svg?style=social"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card artalk-fade-in">
|
||||
<div id="comments"></div>
|
||||
</div>
|
||||
<div class="to-top artalk-fade-in" style="display: none;">TOP</div>
|
||||
<script>
|
||||
var artalk = new Artalk({
|
||||
el: '#comments',
|
||||
placeholder: '来啊,快活啊 (/ω\)',
|
||||
placeholder: '来啊,快活啊 ( ゜- ゜)',
|
||||
defaultAvatar: 'mp',
|
||||
pageKey: 'https://artalk.js.org/',
|
||||
serverUrl: (location.hostname === 'localhost' || location.hostname === '127.0.0.1') ? 'http://localhost:23366/' : 'https://qwqaq.com/Artalk/index.php',
|
||||
@@ -47,5 +55,30 @@
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<script>
|
||||
(function () {
|
||||
const toTopElem = document.querySelector('.to-top')
|
||||
toTopElem.addEventListener('click', () => {
|
||||
window.scrollTo(0, 0)
|
||||
})
|
||||
|
||||
checkShowToTop()
|
||||
document.addEventListener('scroll', () => {
|
||||
checkShowToTop()
|
||||
})
|
||||
|
||||
function checkShowToTop() {
|
||||
const scrollTop = window.scrollY
|
||||
const whereShow = window.screen.height
|
||||
const isNeedShow = (scrollTop > 0) && (scrollTop > whereShow)
|
||||
|
||||
if (isNeedShow) {
|
||||
toTopElem.style.display = ''
|
||||
} else {
|
||||
toTopElem.style.display = 'none'
|
||||
}
|
||||
}
|
||||
})()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "artalk",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.6",
|
||||
"description": "一款简洁有趣的自托管评论系统",
|
||||
"author": "qwqcode <1149527164@qq.com> (https://github.com/qwqcode)",
|
||||
"keywords": [
|
||||
@@ -55,6 +55,7 @@
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"gh-pages": "^2.1.1",
|
||||
"hanabi": "^0.4.0",
|
||||
"html-webpack-harddisk-plugin": "^1.0.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"less": "^3.11.1",
|
||||
"less-loader": "^6.0.0",
|
||||
|
||||
+134
-65
@@ -1,18 +1,23 @@
|
||||
import './css/main.less'
|
||||
import marked from 'marked'
|
||||
import hanabi from 'hanabi'
|
||||
import User from './components/User'
|
||||
import Checker from './components/Checker'
|
||||
import Editor from './components/Editor'
|
||||
import List from './components/List'
|
||||
import Comment from './components/Comment'
|
||||
import Sidebar from './components/Sidebar'
|
||||
import Ui from './utils/ui'
|
||||
import Utils from './utils'
|
||||
import { ArtalkConfig } from '~/types/artalk-config'
|
||||
|
||||
/* global ARTALK_VERSION */
|
||||
|
||||
const defaultOpts: ArtalkConfig = {
|
||||
el: '',
|
||||
placeholder: '来啊,快活啊 (/ω\)',
|
||||
placeholder: '来啊,快活啊 ( ゜- ゜)',
|
||||
noComment: '快来成为第一个评论的人吧~',
|
||||
sendBtn: '发送评论',
|
||||
defaultAvatar: 'mp',
|
||||
pageKey: '',
|
||||
serverUrl: '',
|
||||
@@ -22,26 +27,21 @@ const defaultOpts: ArtalkConfig = {
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
export let ArtalkInstance: Artalk = null
|
||||
|
||||
export default class Artalk {
|
||||
public conf: ArtalkConfig
|
||||
public el: HTMLElement
|
||||
public readonly runId: number = new Date().getTime() // 实例唯一 ID
|
||||
|
||||
public ui: Ui
|
||||
public user: User
|
||||
public checker: Checker
|
||||
public editor: Editor
|
||||
public list: List
|
||||
public sidebar: Sidebar
|
||||
|
||||
public user: {
|
||||
nick: string|null,
|
||||
email: string|null,
|
||||
link: string|null,
|
||||
password: string|null,
|
||||
isAdmin: boolean
|
||||
}
|
||||
public comments: Comment[] = []
|
||||
|
||||
constructor (public conf: ArtalkConfig) {
|
||||
constructor (conf: ArtalkConfig) {
|
||||
// Version Information
|
||||
console.log(`\n %c `
|
||||
+ `Artalk v${ARTALK_VERSION} %c 一款简洁有趣的自托管评论系统 \n\n%c`
|
||||
@@ -50,10 +50,8 @@ export default class Artalk {
|
||||
+ `> https://qwqaq.com\n`,
|
||||
'color: #FFF; background: #1DAAFF; padding:5px 0;', 'color: #FFF; background: #656565; padding:5px 0;', '')
|
||||
|
||||
ArtalkInstance = this
|
||||
|
||||
// Options
|
||||
this.conf = Object.assign(defaultOpts, this.conf)
|
||||
this.conf = { ...defaultOpts, ...conf }
|
||||
|
||||
// Main Element
|
||||
try {
|
||||
@@ -67,68 +65,87 @@ export default class Artalk {
|
||||
}
|
||||
|
||||
this.el.classList.add('artalk')
|
||||
this.el.setAttribute('artalk-run-id', this.runId.toString())
|
||||
|
||||
// 若该元素中 artalk 已装载
|
||||
if (this.el.innerHTML.trim() !== '') this.el.innerHTML = ''
|
||||
|
||||
// Components
|
||||
this.ui = new Ui()
|
||||
this.initUser()
|
||||
this.ui = new Ui(this)
|
||||
this.user = new User(this)
|
||||
this.checker = new Checker(this)
|
||||
this.initMarked()
|
||||
this.editor = new Editor()
|
||||
this.list = new List()
|
||||
this.sidebar = new Sidebar()
|
||||
this.editor = new Editor(this)
|
||||
this.list = new List(this)
|
||||
this.sidebar = new Sidebar(this)
|
||||
|
||||
// 请求获取评论
|
||||
this.list.reqComments()
|
||||
|
||||
// 锚点快速跳转评论
|
||||
window.addEventListener('hashchange', () => {
|
||||
this.checkGoToCommentByUrlHash()
|
||||
})
|
||||
}
|
||||
|
||||
/** 初始化用户数据 */
|
||||
initUser () {
|
||||
// 从 localStorage 导入
|
||||
const localUser = JSON.parse(window.localStorage.getItem('ArtalkUser') || '{}')
|
||||
this.user = {
|
||||
nick: localUser.nick || '',
|
||||
email: localUser.email || '',
|
||||
link: localUser.link || '',
|
||||
password: localUser.password || '',
|
||||
isAdmin: localUser.isAdmin || false
|
||||
}
|
||||
/** 遍历操作 Comment (包括子评论) */
|
||||
public eachComment (commentList: Comment[], action: (comment?: Comment, levelList?: Comment[]) => boolean|void) {
|
||||
if (commentList.length === 0) return
|
||||
commentList.every((item) => {
|
||||
if (action(item, commentList) === false) return false
|
||||
this.eachComment(item.getChildren(), action)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/** 保存用户到 localStorage 中 */
|
||||
saveUser () {
|
||||
window.localStorage.setItem('ArtalkUser', JSON.stringify(this.user))
|
||||
}
|
||||
/** 查找评论项 */
|
||||
public findComment (id: number) {
|
||||
let comment: Comment|null = null
|
||||
|
||||
/** 是否已填写基本用户信息 */
|
||||
checkHasBasicUserInfo () {
|
||||
return !!this.user.nick && !!this.user.email
|
||||
}
|
||||
|
||||
public marked: (src: string, callback?: (error: any, parseResult: string) => void) => string
|
||||
|
||||
private initMarked () {
|
||||
const renderer = new marked.Renderer()
|
||||
const linkRenderer = renderer.link
|
||||
renderer.link = (href, title, text) => {
|
||||
const html = linkRenderer.call(renderer, href, title, text)
|
||||
return html.replace(/^<a /, '<a target="_blank" rel="nofollow" ')
|
||||
}
|
||||
|
||||
const nMarked = marked
|
||||
nMarked.setOptions({
|
||||
renderer,
|
||||
highlight: (code) => {
|
||||
return hanabi(code)
|
||||
},
|
||||
pedantic: false,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: true,
|
||||
sanitize: true, // 净化
|
||||
smartLists: true,
|
||||
smartypants: true,
|
||||
xhtml: false
|
||||
this.eachComment(this.comments, (item) => {
|
||||
if (item.data.id === id) {
|
||||
comment = item
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
this.marked = nMarked
|
||||
return comment
|
||||
}
|
||||
|
||||
/** 获取评论总数 */
|
||||
public getCommentCount (): number {
|
||||
let count = 0
|
||||
this.eachComment(this.comments, () => { count++ })
|
||||
return count
|
||||
}
|
||||
|
||||
/** 删除评论 */
|
||||
public deleteComment (comment: number|Comment) {
|
||||
let findComment: Comment
|
||||
if (typeof comment === 'number') {
|
||||
findComment = this.findComment(comment)
|
||||
if (!findComment) throw Error(`未找到评论 ${comment}`)
|
||||
} else findComment = comment
|
||||
|
||||
findComment.getElem().remove()
|
||||
this.eachComment(this.comments, (item, levelList) => {
|
||||
if (item === findComment) {
|
||||
levelList.splice(levelList.indexOf(item), 1)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空所有评论 */
|
||||
public clearComments () {
|
||||
this.list.commentsWrapEl.innerHTML = ''
|
||||
this.list.data = undefined
|
||||
this.comments = []
|
||||
}
|
||||
|
||||
/** 公共请求 */
|
||||
public request (action: string, data: object, before: () => void, after: () => void, success: (msg: string, data: any) => void, error: (msg: string, data: any) => void) {
|
||||
before()
|
||||
|
||||
@@ -161,4 +178,56 @@ export default class Artalk {
|
||||
|
||||
xhr.send(formData)
|
||||
}
|
||||
|
||||
/** 跳到评论项位置 - 根据 `location.hash` */
|
||||
public checkGoToCommentByUrlHash () {
|
||||
let commentId: number = Number(Utils.getLocationParmByName('artalk_comment'))
|
||||
if (!commentId) {
|
||||
const match = window.location.hash.match(/#artalk-comment-([0-9]+)/)
|
||||
if (!match || !match[1] || Number.isNaN(Number(match[1]))) return
|
||||
commentId = Number(match[1])
|
||||
}
|
||||
|
||||
const comment = this.findComment(commentId)
|
||||
if (!comment && this.list.hasMoreComments) {
|
||||
this.list.readMore()
|
||||
return
|
||||
}
|
||||
if (!comment) { return }
|
||||
|
||||
this.ui.scrollIntoView(comment.getElem(), false)
|
||||
setTimeout(() => {
|
||||
comment.getElem().classList.add('artalk-flash-once')
|
||||
}, 800)
|
||||
}
|
||||
|
||||
public marked: (src: string, callback?: (error: any, parseResult: string) => void) => string
|
||||
|
||||
/** 初始化 Marked */
|
||||
private initMarked () {
|
||||
const renderer = new marked.Renderer()
|
||||
const linkRenderer = renderer.link
|
||||
renderer.link = (href, title, text) => {
|
||||
const html = linkRenderer.call(renderer, href, title, text)
|
||||
return html.replace(/^<a /, '<a target="_blank" rel="nofollow" ')
|
||||
}
|
||||
|
||||
const nMarked = marked
|
||||
nMarked.setOptions({
|
||||
renderer,
|
||||
highlight: (code) => {
|
||||
return hanabi(code)
|
||||
},
|
||||
pedantic: false,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: true,
|
||||
sanitize: true, // 净化
|
||||
smartLists: true,
|
||||
smartypants: true,
|
||||
xhtml: false
|
||||
})
|
||||
|
||||
this.marked = nMarked
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
import Artalk, { ArtalkInstance } from './Artalk'
|
||||
import Artalk from './Artalk'
|
||||
|
||||
export default class ArtalkContext {
|
||||
protected artalk: Artalk = ArtalkInstance
|
||||
public artalk: Artalk
|
||||
|
||||
public constructor (artalk: Artalk) {
|
||||
this.artalk = artalk
|
||||
}
|
||||
}
|
||||
+52
-36
@@ -9,13 +9,13 @@
|
||||
"流口水": " ̄﹃ ̄",
|
||||
"捂脸": "(/ω\)",
|
||||
"给跪": "∠( ᐛ 」∠)_",
|
||||
"蛤?": "(๑•̀ㅁ•́ฅ)",
|
||||
"哈?": "(๑•̀ㅁ•́ฅ)",
|
||||
"斜眼": "→_→",
|
||||
"加油": "୧(๑•̀⌄•́๑)૭",
|
||||
"有木有WiFi": "٩(ˊᗜˋ*)و",
|
||||
"前方高能预警": "(ノ°ο°)ノ",
|
||||
"我从未见过如此厚颜无耻之人": "(´இ皿இ`)",
|
||||
"吓死宝宝惹": "⌇●﹏●⌇",
|
||||
"纳尼": "(´இ皿இ`)",
|
||||
"吓死惹": "⌇●﹏●⌇",
|
||||
"已阅留爪": "(ฅ´ω`ฅ)",
|
||||
"去吧大师球": "(╯°A°)╯︵○○○",
|
||||
"太萌惹": "φ( ̄∇ ̄o)",
|
||||
@@ -41,44 +41,60 @@
|
||||
"inputType": "image",
|
||||
"container": {
|
||||
"原味稽": "https://i.loli.net/2019/02/01/5c53d26b7ae13.png",
|
||||
"微笑默叹以为妙绝": "https://i.loli.net/2019/02/01/5c53daa84f24a.png",
|
||||
"突然滑稽": "https://i.loli.net/2019/02/01/5c53cf2a457f1.jpg",
|
||||
"滑稽摇摆": "https://i.loli.net/2019/02/01/5c53d1904dcb2.gif",
|
||||
"高兴": "https://i.loli.net/2019/02/01/5c53d1b9e5f38.jpg",
|
||||
"惊讶": "https://i.loli.net/2019/02/01/5c53d1e2ad89f.jpg",
|
||||
"紧张": "https://i.loli.net/2017/02/05/5896e8a408253.jpg",
|
||||
"可这和我的帅有什么关系": "https://i.loli.net/2017/02/05/5896ece29a8e0.jpg",
|
||||
"相视一笑": "https://i.loli.net/2019/02/01/5c53d5a093149.jpg",
|
||||
"气稽败坏": "https://i.loli.net/2019/02/01/5c53d216f3c60.jpg",
|
||||
"嘤雄豪杰": "https://i.loli.net/2019/02/01/5c53d08faa9e8.jpg",
|
||||
"笑死": "https://i.loli.net/2019/02/01/5c53d1560e13d.gif",
|
||||
"啊哈哈": "https://i.loli.net/2019/02/01/5c53dc2947d84.jpg",
|
||||
"暗中观察": "https://i.loli.net/2019/02/01/5c53dd21a2e7b.jpg",
|
||||
"吃鱼": "https://i.loli.net/2017/02/05/5896ec2cb7f39.gif",
|
||||
"跃跃欲试": "https://i.loli.net/2017/02/05/5896e9710dfd5.jpg",
|
||||
"跃跃欲试 2": "https://i.loli.net/2019/02/01/5c53dcc057350.jpg",
|
||||
"一时语塞": "https://i.loli.net/2019/02/01/5c53d5d28e22c.jpg",
|
||||
"无法接受": "https://i.loli.net/2019/02/01/5c53cee8422fc.jpg",
|
||||
"坐地": "https://i.loli.net/2019/02/01/5c53dbae85687.jpg",
|
||||
"坐地 2": "https://i.loli.net/2019/02/01/5c53dbe510bcf.jpg",
|
||||
"绝望": "https://i.loli.net/2019/02/01/5c53dc0ba2303.jpg",
|
||||
"蠕动": "https://i.loli.net/2017/02/05/5896e9712a3c1.gif",
|
||||
"委屈": "https://i.loli.net/2019/02/01/5c53d76e250da.jpg",
|
||||
"还是算了": "https://i.loli.net/2019/02/01/5c53db746b2c8.jpg",
|
||||
"还是算了": "https://i.loli.net/2020/04/30/riySFlu75fJdG4p.png",
|
||||
"蓝纹稽": "https://i.loli.net/2020/04/30/jyh5IVzpqXsHuvU.jpg",
|
||||
"随稽应变": "https://i.loli.net/2017/02/05/5896e6ec1d528.jpg",
|
||||
"还是喝酒": "https://i.loli.net/2019/02/01/5c53d78c3f4a5.jpg",
|
||||
"又不是不能用": "https://i.loli.net/2019/02/01/5c53ce897ab55.jpg",
|
||||
"蠕动": "https://i.loli.net/2017/02/05/5896e9712a3c1.gif",
|
||||
"束手无稽": "https://i.loli.net/2020/04/30/dF8sTOpgomj7qf5.jpg",
|
||||
"微笑默叹以为妙绝": "https://i.loli.net/2019/02/01/5c53daa84f24a.png",
|
||||
"喝嘤料": "https://i.loli.net/2019/02/01/5c53d63d8c6af.jpg",
|
||||
"暗中观察": "https://i.loli.net/2019/02/01/5c53dd21a2e7b.jpg",
|
||||
"高兴": "https://i.loli.net/2019/02/01/5c53d1b9e5f38.jpg",
|
||||
"惊稽": "https://i.loli.net/2019/02/01/5c53d1e2ad89f.jpg",
|
||||
"可这和我的帅有什么关系": "https://i.loli.net/2017/02/05/5896ece29a8e0.jpg",
|
||||
"狱稽": "https://i.loli.net/2020/04/30/cUEQrVYGFiDjqhy.jpg",
|
||||
"梆": "https://i.loli.net/2020/04/30/TlAGjm6IvJSMVpq.jpg",
|
||||
"吃鱼摆摆": "https://i.loli.net/2017/02/05/5896ec2cb7f39.gif",
|
||||
"跃跃欲试 3": "https://i.loli.net/2017/02/05/5896ece2ac5a2.gif",
|
||||
"突然滑稽": "https://i.loli.net/2019/02/01/5c53cf2a457f1.jpg",
|
||||
"扶墙怂": "https://i.loli.net/2017/02/05/5896ece2ab57a.jpg",
|
||||
"阔以": "https://i.loli.net/2020/04/30/7EYyq1TcBKa3eQ2.jpg",
|
||||
"不得行": "https://i.loli.net/2020/04/30/KoqBGauX7TEfeyn.jpg",
|
||||
"少儿不宜": "https://i.loli.net/2020/04/30/nt2ZWRozUNjBxAK.jpg",
|
||||
"稽日可期": "https://i.loli.net/2020/04/30/FmfYcoMJesi2Ddq.jpg",
|
||||
"哎": "https://i.loli.net/2020/04/30/ps7PTIANgSErqnU.jpg",
|
||||
"别看丢人": "https://i.loli.net/2019/02/01/5c53d4f89ea29.jpg",
|
||||
"地稽 2": "https://i.loli.net/2019/02/01/5c53dbae85687.jpg",
|
||||
"地稽": "https://i.loli.net/2020/04/30/BnTMX35EPxleVmA.jpg",
|
||||
"老阔有点扣": "https://i.loli.net/2020/04/30/fhDXbA9T1zJPlKk.gif",
|
||||
"啊哈哈": "https://i.loli.net/2019/02/01/5c53dc2947d84.jpg",
|
||||
"无稽可奈": "https://i.loli.net/2020/04/30/UyxTzB2fS3LtH7Q.jpg",
|
||||
"老实巴交": "https://i.loli.net/2020/04/30/7DgSoyqwtYBxchE.jpg",
|
||||
"紧张": "https://i.loli.net/2017/02/05/5896e8a408253.jpg",
|
||||
"摇摆稽": "https://i.loli.net/2019/02/01/5c53d1904dcb2.gif",
|
||||
"又不是不能用": "https://i.loli.net/2019/02/01/5c53ce897ab55.jpg",
|
||||
"一时滑稽": "https://i.loli.net/2019/02/01/5c53d5d28e22c.jpg",
|
||||
"无法接受": "https://i.loli.net/2019/02/01/5c53cee8422fc.jpg",
|
||||
"嘤雄豪稽": "https://i.loli.net/2020/04/30/sbtw6o7iKaM4Nmq.jpg",
|
||||
"相视双稽": "https://i.loli.net/2019/02/01/5c53d5a093149.jpg",
|
||||
"稽皮发麻": "https://i.loli.net/2017/02/05/5896ece2a019f.jpg",
|
||||
"地稽 3": "https://i.loli.net/2019/02/01/5c53dbe510bcf.jpg",
|
||||
"地稽委屈": "https://i.loli.net/2019/02/01/5c53d76e250da.jpg",
|
||||
"地稽抚摸": "https://i.loli.net/2020/04/30/cavZ6nNzMPimLy7.gif",
|
||||
"地稽捶打": "https://i.loli.net/2020/04/30/vFVPynXaHR5sitk.gif",
|
||||
"绝望": "https://i.loli.net/2019/02/01/5c53dc0ba2303.jpg",
|
||||
"气稽败坏": "https://i.loli.net/2019/02/01/5c53d216f3c60.jpg",
|
||||
"当场去世": "https://i.loli.net/2020/04/30/sogxHMTFWbE2lrP.jpg",
|
||||
"喝酒": "https://i.loli.net/2019/02/01/5c53d78c3f4a5.jpg",
|
||||
"老衲摆摊算命": "https://i.loli.net/2017/02/05/5896ece29d8a5.gif",
|
||||
"老哥,稳": "https://i.loli.net/2017/02/05/5896ece29ebb0.jpg",
|
||||
"自闭了": "https://i.loli.net/2019/02/01/5c53d6603ee24.jpg",
|
||||
"自闭稽": "https://i.loli.net/2019/02/01/5c53d6603ee24.jpg",
|
||||
"无话可说": "https://i.loli.net/2019/02/01/5c53d6a77b7e4.jpg",
|
||||
"头皮发麻": "https://i.loli.net/2017/02/05/5896ece2a019f.jpg",
|
||||
"哎~": "https://i.loli.net/2019/02/01/5c53d7c08a2c0.jpg",
|
||||
"别看,丢人!": "https://i.loli.net/2019/02/01/5c53d4f89ea29.jpg",
|
||||
"瑟瑟发抖": "https://i.loli.net/2017/02/05/5896ece2ab57a.jpg",
|
||||
"跃跃欲试?": "https://i.loli.net/2017/02/05/5896ece2ac5a2.gif",
|
||||
"稽之舞": "https://i.loli.net/2019/02/01/5c53de1a4d14d.gif"
|
||||
"跃跃欲试": "https://i.loli.net/2017/02/05/5896e9710dfd5.jpg",
|
||||
"跃跃欲试 2": "https://i.loli.net/2019/02/01/5c53dcc057350.jpg",
|
||||
"满脑子骚操作": "https://i.loli.net/2020/04/30/xJXcUtO2BryHAsa.gif",
|
||||
"稽之舞": "https://i.loli.net/2019/02/01/5c53de1a4d14d.gif",
|
||||
"将稽就稽": "https://i.loli.net/2020/04/30/KVwf8qCrZts6WOT.gif"
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-21
@@ -1,7 +1,6 @@
|
||||
import ArtalkContext from '../ArtalkContext'
|
||||
import Utils from '../utils'
|
||||
import Layer from './Layer'
|
||||
import { ArtalkInstance } from '../Artalk'
|
||||
import BuildLayer from './Layer'
|
||||
|
||||
interface CheckerItem {
|
||||
elem?: HTMLElement
|
||||
@@ -13,32 +12,32 @@ interface CheckerItem {
|
||||
}
|
||||
|
||||
export default class Checker extends ArtalkContext {
|
||||
public static submitCaptchaVal: string
|
||||
public static submitCaptchaImgData: string
|
||||
public submitCaptchaVal: string
|
||||
public submitCaptchaImgData: string
|
||||
|
||||
private static readonly LIST: {[key: string]: CheckerItem} = {
|
||||
private readonly LIST: {[key: string]: CheckerItem} = {
|
||||
'管理员': {
|
||||
body: () => Utils.createElement('<span>敲入密码来验证管理员身份:</span>'),
|
||||
reqAct: 'AdminCheck',
|
||||
reqObj: (inputVal) => {
|
||||
return {
|
||||
nick: ArtalkInstance.user.nick,
|
||||
email: ArtalkInstance.user.email,
|
||||
nick: this.artalk.user.data.nick,
|
||||
email: this.artalk.user.data.email,
|
||||
password: inputVal
|
||||
}
|
||||
},
|
||||
onSuccess: (msg, data, inputVal) => {
|
||||
ArtalkInstance.user.isAdmin = true
|
||||
ArtalkInstance.user.password = inputVal
|
||||
ArtalkInstance.editor.saveUser()
|
||||
this.artalk.user.data.isAdmin = true
|
||||
this.artalk.user.data.password = inputVal
|
||||
this.artalk.user.save()
|
||||
}
|
||||
},
|
||||
'验证码': {
|
||||
body: () => {
|
||||
const elem = Utils.createElement(`<span><img class="artalk-captcha-img" src="${Checker.submitCaptchaImgData || ''}" alt="验证码">敲入验证码继续:</span>`)
|
||||
Checker.LIST['验证码'].elem = elem;
|
||||
const elem = Utils.createElement(`<span><img class="artalk-captcha-img" src="${this.submitCaptchaImgData || ''}" alt="验证码">敲入验证码继续:</span>`)
|
||||
this.LIST['验证码'].elem = elem;
|
||||
(elem.querySelector('.artalk-captcha-img') as HTMLElement).onclick = () => {
|
||||
Checker.LIST['验证码'].refresh()
|
||||
this.LIST['验证码'].refresh()
|
||||
}
|
||||
return elem
|
||||
},
|
||||
@@ -49,13 +48,13 @@ export default class Checker extends ArtalkContext {
|
||||
}
|
||||
},
|
||||
onSuccess: (msg, data, inputVal) => {
|
||||
Checker.submitCaptchaVal = inputVal
|
||||
this.submitCaptchaVal = inputVal
|
||||
},
|
||||
refresh: (imgData?: string) => {
|
||||
const { elem } = Checker.LIST['验证码']
|
||||
const { elem } = this.LIST['验证码']
|
||||
const imgEl = elem.querySelector('.artalk-captcha-img')
|
||||
if (!imgData) {
|
||||
ArtalkInstance.request('CaptchaCheck', { refresh: true }, () => {}, () => {}, (msg, data) => {
|
||||
this.artalk.request('CaptchaCheck', { refresh: true }, () => {}, () => {}, (msg, data) => {
|
||||
imgEl.setAttribute('src', data.img_data)
|
||||
}, () => {})
|
||||
} else {
|
||||
@@ -65,8 +64,8 @@ export default class Checker extends ArtalkContext {
|
||||
}
|
||||
}
|
||||
|
||||
public static checkAction (name: '管理员'|'验证码', action: () => void) {
|
||||
const checker = Checker.LIST[name]
|
||||
public action (name: '管理员'|'验证码', action: () => void) {
|
||||
const checker = this.LIST[name]
|
||||
|
||||
const formEl = Utils.createElement()
|
||||
formEl.appendChild(checker.body())
|
||||
@@ -77,7 +76,7 @@ export default class Checker extends ArtalkContext {
|
||||
input.focus() // 延迟以保证有效
|
||||
}, 80)
|
||||
|
||||
const layer = new Layer(`checker-${new Date().getTime()}`)
|
||||
const layer = BuildLayer(this.artalk, `checker-${new Date().getTime()}`)
|
||||
layer.setMaskClickHide(false)
|
||||
layer.show()
|
||||
|
||||
@@ -88,7 +87,7 @@ export default class Checker extends ArtalkContext {
|
||||
}
|
||||
}
|
||||
|
||||
ArtalkInstance.ui.showDialog(layer.getEl(), formEl, (dialogElem, btnElem: HTMLElement) => {
|
||||
this.artalk.ui.showDialog(layer.getEl(), formEl, (dialogElem, btnElem: HTMLElement) => {
|
||||
const inputVal = input.value.trim()
|
||||
const btnRawText = btnElem.innerText
|
||||
const btnTextSet = (btnText: string) => {
|
||||
@@ -100,7 +99,7 @@ export default class Checker extends ArtalkContext {
|
||||
btnElem.classList.remove('error')
|
||||
}
|
||||
|
||||
ArtalkInstance.request(checker.reqAct, checker.reqObj(inputVal), () => {
|
||||
this.artalk.request(checker.reqAct, checker.reqObj(inputVal), () => {
|
||||
btnElem.innerText = '加载中...'
|
||||
}, () => {
|
||||
|
||||
|
||||
+126
-96
@@ -2,11 +2,14 @@ import '../css/comment.less'
|
||||
import List from './List'
|
||||
import UADetect from '../utils/detect'
|
||||
import { CommentData } from '~/types/artalk-data'
|
||||
import Artalk from '../Artalk'
|
||||
import ArtalkContext from '../ArtalkContext'
|
||||
import Utils from '../utils'
|
||||
|
||||
export default class Comment extends ArtalkContext {
|
||||
public elem: HTMLElement
|
||||
public mainEl: HTMLElement
|
||||
public bodyEl: HTMLElement
|
||||
public contentEl: HTMLElement
|
||||
public childrenEl: HTMLElement
|
||||
public actionsEl: HTMLElement
|
||||
@@ -16,55 +19,107 @@ export default class Comment extends ArtalkContext {
|
||||
private readonly maxNestedNo = 3 // 最多嵌套层数
|
||||
public children: Comment[] = []
|
||||
|
||||
constructor (public list: List, public data: CommentData) {
|
||||
super()
|
||||
constructor (artalk: Artalk, public list: List, public data: CommentData) {
|
||||
super(artalk)
|
||||
|
||||
this.data = { ...this.data }
|
||||
|
||||
this.elem = Utils.createElement(require('../templates/Comment.ejs')(this))
|
||||
this.contentEl = this.elem.querySelector('.artalk-content')
|
||||
this.actionsEl = this.elem.querySelector('.artalk-comment-actions')
|
||||
|
||||
this.parent = null
|
||||
this.nestedNum = 1 // 现在已嵌套 n 层
|
||||
|
||||
this.renderElem()
|
||||
}
|
||||
|
||||
private renderElem () {
|
||||
this.elem = Utils.createElement(require('../templates/Comment.ejs')(this))
|
||||
this.mainEl = this.elem.querySelector('.artalk-comment-main')
|
||||
this.bodyEl = this.elem.querySelector('.artalk-body')
|
||||
this.contentEl = this.bodyEl.querySelector('.artalk-content')
|
||||
this.actionsEl = this.elem.querySelector('.artalk-comment-actions')
|
||||
this.childrenEl = null
|
||||
|
||||
// 绑定回复按钮事件
|
||||
this.actionsEl.querySelector('[data-comment-action="reply"]').addEventListener('click', () => {
|
||||
this.artalk.editor.setReply(this)
|
||||
// 已折叠的评论
|
||||
const contentShowBtn = this.mainEl.querySelector('.artalk-collapsed .artalk-show-btn')
|
||||
if (contentShowBtn) {
|
||||
contentShowBtn.addEventListener('click', () => {
|
||||
if (this.contentEl.classList.contains('artalk-hide')) {
|
||||
this.contentEl.classList.remove('artalk-hide')
|
||||
this.artalk.ui.playFadeInAnim(this.contentEl)
|
||||
contentShowBtn.innerHTML = '收起内容'
|
||||
} else {
|
||||
this.contentEl.classList.add('artalk-hide')
|
||||
contentShowBtn.innerHTML = '查看内容'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.initActionBtn()
|
||||
|
||||
return this.elem
|
||||
}
|
||||
|
||||
private refreshUI () {
|
||||
const originalEl = this.elem
|
||||
const newEl = this.renderElem()
|
||||
originalEl.replaceWith(newEl) // 替换 document 中的的 elem
|
||||
this.playFadeInAnim()
|
||||
|
||||
// 重建子评论元素
|
||||
this.artalk.eachComment(this.children, (child) => {
|
||||
child.parent.getChildrenEl().appendChild(child.renderElem())
|
||||
child.playFadeInAnim()
|
||||
})
|
||||
}
|
||||
|
||||
initActionBtn () {
|
||||
// 绑定回复按钮事件
|
||||
const replyBtn = this.actionsEl.querySelector('[data-comment-action="reply"]') as HTMLElement
|
||||
if (replyBtn) {
|
||||
replyBtn.addEventListener('click', () => {
|
||||
this.artalk.editor.setReply(this)
|
||||
})
|
||||
}
|
||||
|
||||
// 绑定折叠按钮事件
|
||||
const collapseBtn = this.actionsEl.querySelector('[data-comment-action="collapse"]') as HTMLElement
|
||||
if (collapseBtn) {
|
||||
collapseBtn.addEventListener('click', () => {
|
||||
this.adminCollapse(collapseBtn)
|
||||
})
|
||||
}
|
||||
|
||||
// 绑定删除按钮事件
|
||||
const actionDelBtnEl: HTMLElement = this.actionsEl.querySelector('[data-comment-action="delete"]')
|
||||
actionDelBtnEl.addEventListener('click', () => {
|
||||
this.adminDelete(this.data.id, actionDelBtnEl)
|
||||
})
|
||||
const delBtn = this.actionsEl.querySelector('[data-comment-action="delete"]') as HTMLElement
|
||||
if (delBtn) {
|
||||
delBtn.addEventListener('click', () => {
|
||||
this.adminDelete(delBtn)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getIsRoot () {
|
||||
return this.parent === null
|
||||
}
|
||||
|
||||
setChild (comment: Comment) {
|
||||
this.children.push(comment)
|
||||
this.getChildrenEl().appendChild(comment.getElem())
|
||||
comment.parent = this
|
||||
comment.nestedNum = this.nestedNum + 1 // 嵌套层数 +1
|
||||
|
||||
comment.playFadeInAnim()
|
||||
this.list.refreshUI()
|
||||
}
|
||||
|
||||
getChildren () {
|
||||
return this.children
|
||||
}
|
||||
|
||||
putChild (childC: Comment) {
|
||||
childC.parent = this
|
||||
childC.nestedNum = this.nestedNum + 1 // 嵌套层数 +1
|
||||
this.children.push(childC)
|
||||
|
||||
this.getChildrenEl().appendChild(childC.getElem())
|
||||
childC.playFadeInAnim()
|
||||
}
|
||||
|
||||
getChildrenEl () {
|
||||
if (this.childrenEl === null) {
|
||||
// console.log(this.nestedNo)
|
||||
if (this.nestedNum < this.maxNestedNo) {
|
||||
this.childrenEl = Utils.createElement('<div class="artalk-comment-children"></div>')
|
||||
this.contentEl.appendChild(this.childrenEl)
|
||||
this.mainEl.appendChild(this.childrenEl)
|
||||
} else {
|
||||
this.childrenEl = this.parent.getChildrenEl()
|
||||
}
|
||||
@@ -92,60 +147,8 @@ export default class Comment extends ArtalkContext {
|
||||
return this.artalk.marked(this.data.content)
|
||||
}
|
||||
|
||||
padWithZeros (vNumber: number, width: number) {
|
||||
let numAsString = vNumber.toString()
|
||||
while (numAsString.length < width) {
|
||||
numAsString = `0${numAsString}`
|
||||
}
|
||||
return numAsString
|
||||
}
|
||||
|
||||
dateFormat (date: Date) {
|
||||
const vDay = this.padWithZeros(date.getDate(), 2)
|
||||
const vMonth = this.padWithZeros(date.getMonth() + 1, 2)
|
||||
const vYear = this.padWithZeros(date.getFullYear(), 2)
|
||||
// var vHour = padWithZeros(date.getHours(), 2);
|
||||
// var vMinute = padWithZeros(date.getMinutes(), 2);
|
||||
// var vSecond = padWithZeros(date.getSeconds(), 2);
|
||||
return `${vYear}-${vMonth}-${vDay}`
|
||||
}
|
||||
|
||||
timeAgo (date: Date) {
|
||||
try {
|
||||
const oldTime = date.getTime()
|
||||
const currTime = new Date().getTime()
|
||||
const diffValue = currTime - oldTime
|
||||
|
||||
const days = Math.floor(diffValue / (24 * 3600 * 1000))
|
||||
if (days === 0) {
|
||||
// 计算相差小时数
|
||||
const leave1 = diffValue % (24 * 3600 * 1000) // 计算天数后剩余的毫秒数
|
||||
const hours = Math.floor(leave1 / (3600 * 1000))
|
||||
if (hours === 0) {
|
||||
// 计算相差分钟数
|
||||
const leave2 = leave1 % (3600 * 1000) // 计算小时数后剩余的毫秒数
|
||||
const minutes = Math.floor(leave2 / (60 * 1000))
|
||||
if (minutes === 0) {
|
||||
// 计算相差秒数
|
||||
const leave3 = leave2 % (60 * 1000) // 计算分钟数后剩余的毫秒数
|
||||
const seconds = Math.round(leave3 / 1000)
|
||||
return `${seconds} 秒前`
|
||||
}
|
||||
return `${minutes} 分钟前`
|
||||
}
|
||||
return `${hours} 小时前`
|
||||
}
|
||||
if (days < 0) return '刚刚'
|
||||
|
||||
if (days < 8) {
|
||||
return `${days} 天前`
|
||||
}
|
||||
|
||||
return this.dateFormat(date)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return ' - '
|
||||
}
|
||||
getDateFormatted () {
|
||||
return Utils.timeAgo(new Date(this.data.date))
|
||||
}
|
||||
|
||||
getUserUaBrowser () {
|
||||
@@ -160,20 +163,46 @@ export default class Comment extends ArtalkContext {
|
||||
|
||||
/** 渐出动画 */
|
||||
playFadeInAnim () {
|
||||
this.elem.classList.add('artalk-fade-in')
|
||||
// 动画结束清除 class
|
||||
const onAnimEnded = () => {
|
||||
this.elem.classList.remove('artalk-fade-in')
|
||||
this.elem.removeEventListener('animationend', onAnimEnded)
|
||||
}
|
||||
this.elem.addEventListener('animationend', onAnimEnded)
|
||||
this.artalk.ui.playFadeInAnim(this.elem)
|
||||
}
|
||||
|
||||
/** 管理员 - 评论折叠 */
|
||||
adminCollapse (btnElem: HTMLElement) {
|
||||
if (btnElem.classList.contains('artalk-in-process')) return // 若正在折叠中
|
||||
const btnTextOrg = btnElem.innerText
|
||||
const isCollapse = !this.data.is_collapsed
|
||||
this.artalk.request('CommentCollapse', {
|
||||
id: this.data.id,
|
||||
nick: this.artalk.user.data.nick,
|
||||
email: this.artalk.user.data.email,
|
||||
password: this.artalk.user.data.password,
|
||||
is_collapsed: Number(isCollapse)
|
||||
}, () => {
|
||||
btnElem.classList.add('artalk-in-process')
|
||||
btnElem.innerText = isCollapse ? '折叠中...' : '展开中...'
|
||||
}, () => {
|
||||
}, (msg, data) => {
|
||||
btnElem.classList.remove('artalk-in-process')
|
||||
this.data.is_collapsed = data.is_collapsed
|
||||
this.artalk.eachComment([this], (item) => {
|
||||
item.data.is_allow_reply = !data.is_collapsed // 禁止回复
|
||||
})
|
||||
this.refreshUI()
|
||||
this.artalk.ui.playFadeInAnim(this.bodyEl)
|
||||
this.list.refreshUI()
|
||||
}, (msg, data) => {
|
||||
btnElem.classList.add('artalk-error')
|
||||
btnElem.innerText = isCollapse ? '折叠失败' : '展开失败'
|
||||
setTimeout(() => {
|
||||
btnElem.innerText = btnTextOrg
|
||||
btnElem.classList.remove('artalk-error')
|
||||
btnElem.classList.remove('artalk-in-process')
|
||||
}, 2000)
|
||||
})
|
||||
}
|
||||
|
||||
/** 管理员 - 评论删除 */
|
||||
adminDelete (commentId: number, btnElem: HTMLElement) {
|
||||
const comment = this.list.findComment(commentId)
|
||||
if (!comment) throw Error(`未找到评论 ${commentId}`)
|
||||
|
||||
adminDelete (btnElem: HTMLElement) {
|
||||
if (btnElem.classList.contains('artalk-in-process')) return // 若正在删除中
|
||||
|
||||
// 删除确认
|
||||
@@ -192,18 +221,19 @@ export default class Comment extends ArtalkContext {
|
||||
}
|
||||
const btnTextOrg = btnElem.innerText
|
||||
this.artalk.request('CommentDel', {
|
||||
id: commentId,
|
||||
nick: this.artalk.user.nick,
|
||||
email: this.artalk.user.email,
|
||||
password: this.artalk.user.password
|
||||
id: this.data.id,
|
||||
nick: this.artalk.user.data.nick,
|
||||
email: this.artalk.user.data.email,
|
||||
password: this.artalk.user.data.password
|
||||
}, () => {
|
||||
btnElem.classList.add('artalk-in-process')
|
||||
btnElem.innerText = '删除中...'
|
||||
}, () => {
|
||||
btnElem.innerText = btnTextOrg
|
||||
}, (msg, data) => {
|
||||
this.list.deleteComment(comment)
|
||||
this.list.refreshUI()
|
||||
btnElem.innerText = btnTextOrg
|
||||
this.artalk.deleteComment(this)
|
||||
this.list.data.total -= 1 // 评论数 -1
|
||||
this.list.refreshUI() // 刷新 list
|
||||
btnElem.classList.remove('artalk-in-process')
|
||||
}, (msg, data) => {
|
||||
btnElem.classList.add('artalk-error')
|
||||
|
||||
+102
-39
@@ -2,21 +2,23 @@ import '../css/editor.less'
|
||||
import Comment from './Comment'
|
||||
import EmoticonsPlug from './editor-plugs/EmoticonsPlug'
|
||||
import PreviewPlug from './editor-plugs/PreviewPlug'
|
||||
import Artalk from '../Artalk'
|
||||
import ArtalkContext from '../ArtalkContext'
|
||||
import Utils from '../utils'
|
||||
import Layer from './Layer'
|
||||
import Checker from './Checker'
|
||||
|
||||
export default class Editor extends ArtalkContext {
|
||||
private readonly LOADABLE_PLUG_LIST = [EmoticonsPlug, PreviewPlug]
|
||||
public plugList: { [name: string]: any }
|
||||
public plugList: { [name: string]: any } = {}
|
||||
|
||||
public el: HTMLElement
|
||||
|
||||
public headerEl: HTMLElement
|
||||
public textareaWrapEl: HTMLElement
|
||||
public textareaEl: HTMLTextAreaElement
|
||||
public closeCommentEl: HTMLTextAreaElement
|
||||
public plugWrapEl: HTMLElement
|
||||
public bottomEl: HTMLElement
|
||||
public bottomPartLeftEl: HTMLElement
|
||||
public plugSwitcherWrapEl: HTMLElement
|
||||
public bottomPartRightEl: HTMLElement
|
||||
@@ -30,8 +32,8 @@ export default class Editor extends ArtalkContext {
|
||||
return this.artalk.user
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
constructor (artalk: Artalk) {
|
||||
super(artalk)
|
||||
|
||||
this.el = Utils.createElement(require('../templates/Editor.ejs')(this))
|
||||
this.artalk.el.appendChild(this.el)
|
||||
@@ -39,7 +41,9 @@ export default class Editor extends ArtalkContext {
|
||||
this.headerEl = this.el.querySelector('.artalk-editor-header')
|
||||
this.textareaWrapEl = this.el.querySelector('.artalk-editor-textarea-wrap')
|
||||
this.textareaEl = this.el.querySelector('.artalk-editor-textarea')
|
||||
this.closeCommentEl = this.el.querySelector('.artalk-close-comment')
|
||||
this.plugWrapEl = this.el.querySelector('.artalk-editor-plug-wrap')
|
||||
this.bottomEl = this.el.querySelector('.artalk-editor-bottom')
|
||||
this.bottomPartLeftEl = this.el.querySelector('.artalk-editor-bottom-part.artalk-left')
|
||||
this.plugSwitcherWrapEl = this.el.querySelector('.artalk-editor-plug-switcher-wrap')
|
||||
this.bottomPartRightEl = this.el.querySelector('.artalk-editor-bottom-part.artalk-right')
|
||||
@@ -65,36 +69,40 @@ export default class Editor extends ArtalkContext {
|
||||
}
|
||||
|
||||
initHeader () {
|
||||
Object.keys(this.user).forEach((field) => {
|
||||
Object.keys(this.user.data).forEach((field) => {
|
||||
const inputEl: HTMLInputElement = this.headerEl.querySelector(`[name="${field}"]`)
|
||||
if (inputEl !== null && inputEl instanceof HTMLInputElement) {
|
||||
inputEl.value = this.user[field] || ''
|
||||
inputEl.value = this.user.data[field] || ''
|
||||
// 输入框内容变化事件
|
||||
inputEl.addEventListener('input', (evt) => {
|
||||
this.user[field] = inputEl.value.trim()
|
||||
this.user.password = ''
|
||||
this.user.isAdmin = false
|
||||
this.user.data[field] = inputEl.value.trim()
|
||||
|
||||
// 若修改的是 nick or email
|
||||
if (field !== 'link') {
|
||||
this.user.data.password = ''
|
||||
this.user.data.isAdmin = false
|
||||
if (this.user.checkHasBasicUserInfo()
|
||||
&& this.artalk.list.checkNickEmailIsAdmin(this.user.data.nick, this.user.data.email)) {
|
||||
// 昵称为管理员,显示管理员密码验证 dialog
|
||||
this.artalk.checker.action('管理员', () => {
|
||||
this.artalk.list.refreshUI()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.saveUser()
|
||||
this.artalk.list.refreshUI()
|
||||
|
||||
if (this.artalk.checkHasBasicUserInfo()
|
||||
&& this.artalk.list.checkNickEmailIsAdmin(this.user.nick, this.user.email)) {
|
||||
// 昵称为管理员,显示管理员密码验证 dialog
|
||||
Checker.checkAction('管理员', () => {
|
||||
this.artalk.list.refreshUI()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
saveUser () {
|
||||
this.artalk.saveUser()
|
||||
this.user.save()
|
||||
}
|
||||
|
||||
saveContent () {
|
||||
window.localStorage.setItem('ArtalkContent', this.getContent().trim())
|
||||
window.localStorage.setItem('ArtalkContent', this.getContentOriginal().trim())
|
||||
}
|
||||
|
||||
initTextarea () {
|
||||
@@ -110,15 +118,24 @@ export default class Editor extends ArtalkContext {
|
||||
|
||||
// 输入框高度随内容而变化
|
||||
this.textareaEl.addEventListener('input', (evt) => {
|
||||
const diff = this.textareaEl.offsetHeight - this.textareaEl.clientHeight
|
||||
this.textareaEl.style.height = '0px' // it's a magic. 若不加此行,内容减少,高度回不去
|
||||
this.textareaEl.style.height = `${this.textareaEl.scrollHeight + diff}px`
|
||||
this.adjustTextareaHeight()
|
||||
})
|
||||
}
|
||||
|
||||
adjustTextareaHeight () {
|
||||
const diff = this.textareaEl.offsetHeight - this.textareaEl.clientHeight
|
||||
this.textareaEl.style.height = '0px' // it's a magic. 若不加此行,内容减少,高度回不去
|
||||
this.textareaEl.style.height = `${this.textareaEl.scrollHeight + diff}px`
|
||||
}
|
||||
|
||||
openedPlugName: string = null
|
||||
|
||||
initEditorPlug () {
|
||||
this.plugList = {}
|
||||
let openedPlugName = null
|
||||
this.plugWrapEl.innerHTML = ''
|
||||
this.plugWrapEl.style.display = 'none'
|
||||
this.openedPlugName = null
|
||||
this.plugSwitcherWrapEl.innerHTML = ''
|
||||
|
||||
// 依次实例化 plug
|
||||
this.LOADABLE_PLUG_LIST.forEach((PlugObj) => {
|
||||
@@ -132,10 +149,10 @@ export default class Editor extends ArtalkContext {
|
||||
this.plugSwitcherWrapEl.querySelectorAll('.active').forEach(item => item.classList.remove('active'))
|
||||
|
||||
// 若点击已打开的,则收起
|
||||
if (plug.getName() === openedPlugName) {
|
||||
if (plug.getName() === this.openedPlugName) {
|
||||
plug.onHide()
|
||||
this.plugWrapEl.style.display = 'none'
|
||||
openedPlugName = null
|
||||
this.openedPlugName = null
|
||||
return
|
||||
}
|
||||
|
||||
@@ -159,13 +176,20 @@ export default class Editor extends ArtalkContext {
|
||||
})
|
||||
|
||||
this.plugWrapEl.style.display = ''
|
||||
openedPlugName = plug.getName()
|
||||
this.openedPlugName = plug.getName()
|
||||
|
||||
btnElem.classList.add('active')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/** 关闭编辑器插件 */
|
||||
closePlug () {
|
||||
this.plugWrapEl.innerHTML = ''
|
||||
this.plugWrapEl.style.display = 'none'
|
||||
this.openedPlugName = null
|
||||
}
|
||||
|
||||
insertContent (val: string) {
|
||||
if ((document as any).selection) {
|
||||
this.textareaEl.focus();
|
||||
@@ -192,6 +216,7 @@ export default class Editor extends ArtalkContext {
|
||||
if (!!this.plugList && !!this.plugList.preview) {
|
||||
this.plugList.preview.updateContent()
|
||||
}
|
||||
this.adjustTextareaHeight()
|
||||
}
|
||||
|
||||
clearEditor () {
|
||||
@@ -200,7 +225,19 @@ export default class Editor extends ArtalkContext {
|
||||
}
|
||||
|
||||
getContent () {
|
||||
return this.textareaEl.value || ''
|
||||
let content = this.getContentOriginal()
|
||||
|
||||
// 表情包处理
|
||||
if (this.plugList && this.plugList.emoticons) {
|
||||
const emoticonsPlug = this.plugList.emoticons as EmoticonsPlug
|
||||
content = emoticonsPlug.transEmoticonImageText(content)
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
getContentOriginal () {
|
||||
return this.textareaEl.value || '' // Tip: !!"0" === true
|
||||
}
|
||||
|
||||
getContentMarked () {
|
||||
@@ -262,36 +299,40 @@ export default class Editor extends ArtalkContext {
|
||||
|
||||
this.artalk.request('CommentAdd', {
|
||||
content: this.getContent(),
|
||||
nick: this.user.nick,
|
||||
email: this.user.email,
|
||||
link: this.user.link,
|
||||
nick: this.user.data.nick,
|
||||
email: this.user.data.email,
|
||||
link: this.user.data.link,
|
||||
rid: this.getReplyComment() === null ? 0 : this.getReplyComment().data.id,
|
||||
page_key: this.artalk.conf.pageKey,
|
||||
password: this.user.password,
|
||||
captcha: Checker.submitCaptchaVal || ''
|
||||
password: this.user.data.password,
|
||||
captcha: this.artalk.checker.submitCaptchaVal || ''
|
||||
}, () => {
|
||||
this.artalk.ui.showLoading(this.el)
|
||||
}, () => {
|
||||
this.artalk.ui.hideLoading(this.el)
|
||||
}, (msg, data) => {
|
||||
const newComment = new Comment(this.artalk.list, data.comment)
|
||||
const newComment = new Comment(this.artalk, this.artalk.list, data.comment)
|
||||
if (this.getReplyComment() !== null) {
|
||||
this.getReplyComment().setChild(newComment)
|
||||
this.getReplyComment().putChild(newComment)
|
||||
} else {
|
||||
this.artalk.list.putComment(newComment)
|
||||
this.artalk.list.putRootComment(newComment)
|
||||
}
|
||||
this.clearEditor() // 清空编辑器
|
||||
|
||||
this.artalk.ui.scrollIntoView(newComment.getElem())
|
||||
this.clearEditor()
|
||||
newComment.playFadeInAnim() // 播放评论渐出动画
|
||||
this.artalk.list.data.total += 1 // 评论数增加 1
|
||||
this.artalk.list.refreshUI() // 更新 list 界面
|
||||
}, (msg, data) => {
|
||||
if ((typeof data === 'object') && data !== null && typeof data.need_password === 'boolean' && data.need_password === true) {
|
||||
// 管理员密码验证
|
||||
Checker.checkAction('管理员', () => {
|
||||
this.artalk.checker.action('管理员', () => {
|
||||
this.submit()
|
||||
})
|
||||
} else if ((typeof data === 'object') && data !== null && typeof data.need_captcha === 'boolean' && data.need_captcha === true) {
|
||||
// 验证码验证
|
||||
Checker.submitCaptchaImgData = data.img_data
|
||||
Checker.checkAction('验证码', () => {
|
||||
this.artalk.checker.submitCaptchaImgData = data.img_data
|
||||
this.artalk.checker.action('验证码', () => {
|
||||
this.submit()
|
||||
})
|
||||
} else {
|
||||
@@ -303,5 +344,27 @@ export default class Editor extends ArtalkContext {
|
||||
showNotify (msg: string, type) {
|
||||
this.artalk.ui.showNotify(msg, type, this.notifyWrapEl)
|
||||
}
|
||||
|
||||
/** 关闭评论 */
|
||||
closeComment () {
|
||||
this.closeCommentEl.style.display = ''
|
||||
|
||||
if (!this.artalk.user.data.isAdmin) {
|
||||
this.textareaEl.style.display = 'none'
|
||||
this.closePlug()
|
||||
this.bottomEl.style.display = 'none'
|
||||
} else {
|
||||
// 管理员一直打开评论
|
||||
this.textareaEl.style.display = ''
|
||||
this.bottomEl.style.display = ''
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开评论 */
|
||||
openComment () {
|
||||
this.closeCommentEl.style.display = 'none'
|
||||
this.textareaEl.style.display = ''
|
||||
this.bottomEl.style.display = ''
|
||||
}
|
||||
}
|
||||
|
||||
+23
-16
@@ -1,30 +1,33 @@
|
||||
import Artalk from '../Artalk'
|
||||
import ArtalkContext from '../ArtalkContext'
|
||||
import Utils from '../utils'
|
||||
|
||||
export default class Layer extends ArtalkContext {
|
||||
// TODO: for 一页多评论,该实例请用完及时销毁;待优化
|
||||
export class Layer extends ArtalkContext {
|
||||
private el: HTMLElement
|
||||
private wrapEl: HTMLElement
|
||||
private maskEl: HTMLElement
|
||||
|
||||
private maskClickHideEnable: boolean = true
|
||||
|
||||
constructor (private name: string, html?: HTMLElement) {
|
||||
super()
|
||||
constructor (artalk: Artalk, private name: string, html?: HTMLElement) {
|
||||
super(artalk)
|
||||
this.initWrap()
|
||||
|
||||
this.el = this.wrapEl.querySelector(
|
||||
`[data-layer-name="${name}"]`
|
||||
)
|
||||
if (this.el !== null) throw new Error(`layer "${name}" 已存在`)
|
||||
if (!html) {
|
||||
this.el = Utils.createElement()
|
||||
this.el.classList.add('artalk-layer-item')
|
||||
} else {
|
||||
this.el = html
|
||||
this.el = this.wrapEl.querySelector(`[data-layer-name="${name}"]`)
|
||||
if (this.el === null) {
|
||||
// 若 layer 元素未创建过
|
||||
if (!html) {
|
||||
this.el = Utils.createElement()
|
||||
this.el.classList.add('artalk-layer-item')
|
||||
} else {
|
||||
this.el = html
|
||||
}
|
||||
}
|
||||
this.el.setAttribute('data-layer-name', name)
|
||||
this.el.style.display = 'none'
|
||||
|
||||
// 添加到 layers wrap 中
|
||||
this.wrapEl.prepend(this.el)
|
||||
}
|
||||
|
||||
@@ -32,7 +35,7 @@ export default class Layer extends ArtalkContext {
|
||||
this.wrapEl = document.querySelector(`.artalk-layer-wrap`)
|
||||
if (!this.wrapEl) {
|
||||
this.wrapEl = Utils.createElement(
|
||||
'<div class="artalk-layer-wrap" style="display: none;"><div class="artalk-layer-mask"></div></div>'
|
||||
`<div class="artalk-layer-wrap" style="display: none;"><div class="artalk-layer-mask"></div></div>`
|
||||
)
|
||||
document.body.appendChild(this.wrapEl)
|
||||
}
|
||||
@@ -52,6 +55,8 @@ export default class Layer extends ArtalkContext {
|
||||
return this.el
|
||||
}
|
||||
|
||||
private static hideTimeoutList: number[] = []
|
||||
|
||||
show () {
|
||||
Layer.hideTimeoutList.forEach(item => {
|
||||
clearTimeout(item)
|
||||
@@ -70,11 +75,10 @@ export default class Layer extends ArtalkContext {
|
||||
document.body.style.overflow = 'hidden'
|
||||
}
|
||||
|
||||
private static hideTimeoutList: number[] = []
|
||||
|
||||
hide () {
|
||||
Layer.hideTimeoutList.push(setTimeout(() => {
|
||||
this.wrapEl.style.display = 'none'
|
||||
document.body.style.overflow = ''
|
||||
}, 450))
|
||||
|
||||
this.wrapEl.classList.add('artalk-fade-out')
|
||||
@@ -84,7 +88,6 @@ export default class Layer extends ArtalkContext {
|
||||
}, 200))
|
||||
|
||||
this.el.style.display = 'none'
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
|
||||
setMaskClickHide (enable: boolean) {
|
||||
@@ -97,3 +100,7 @@ export default class Layer extends ArtalkContext {
|
||||
this.el = null
|
||||
}
|
||||
}
|
||||
|
||||
export default function BuildLayer (artalk: Artalk, name: string, html?: HTMLElement) {
|
||||
return new Layer(artalk, name, html)
|
||||
}
|
||||
+92
-155
@@ -1,44 +1,37 @@
|
||||
import md5 from 'md5'
|
||||
import '../css/list.less'
|
||||
import Comment from './Comment'
|
||||
import { ListData } from '~/types/artalk-data'
|
||||
import { ListData, CommentData } from '~/types/artalk-data'
|
||||
import Artalk from '../Artalk'
|
||||
import ArtalkContext from '../ArtalkContext'
|
||||
import Utils from '../utils'
|
||||
|
||||
export default class List extends ArtalkContext {
|
||||
public el: HTMLElement
|
||||
public commentsWrapEl: HTMLElement
|
||||
public comments: Comment[] = []
|
||||
|
||||
public data: ListData
|
||||
public reqPageSize: number = 15 // 每次请求获取量
|
||||
|
||||
public readMoreEl: HTMLElement
|
||||
public readMoreLoadingEl: HTMLElement
|
||||
public readMoreTextEl: HTMLElement
|
||||
|
||||
public data: ListData
|
||||
public reqPageSize: number = 15 // 每次请求获取量
|
||||
|
||||
public isLoading: boolean = false
|
||||
|
||||
public closeCommentBtnEl: HTMLElement
|
||||
public openSidebarBtnEl: HTMLElement
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
constructor (artalk: Artalk) {
|
||||
super(artalk)
|
||||
|
||||
this.el = Utils.createElement(require('../templates/List.ejs')(this))
|
||||
this.artalk.el.appendChild(this.el)
|
||||
|
||||
this.commentsWrapEl = this.el.querySelector('.artalk-list-comments-wrap')
|
||||
|
||||
// 侧边栏呼出按钮
|
||||
this.openSidebarBtnEl = this.el.querySelector('[data-action="open-sidebar"]')
|
||||
this.openSidebarBtnEl.addEventListener('click', () => {
|
||||
this.artalk.sidebar.show()
|
||||
})
|
||||
|
||||
// 锚点快速跳转评论
|
||||
window.addEventListener('hashchange', () => {
|
||||
this.checkRedirectByUrlHash()
|
||||
})
|
||||
// 操作按钮
|
||||
this.initListActionBtn()
|
||||
|
||||
// 查看更多
|
||||
this.reqPageSize = this.artalk.conf.readMore ? (this.artalk.conf.readMore.pageSize || this.reqPageSize) : this.reqPageSize
|
||||
@@ -49,15 +42,12 @@ export default class List extends ArtalkContext {
|
||||
this.readMoreEl.addEventListener('click', () => {
|
||||
this.readMore()
|
||||
})
|
||||
|
||||
// 请求获取评论
|
||||
this.reqComments()
|
||||
}
|
||||
|
||||
/** 拉取评论 */
|
||||
reqComments (offset: Number = 0) {
|
||||
public reqComments (offset: Number = 0) {
|
||||
if (offset === 0) {
|
||||
this.clearComments();
|
||||
this.artalk.clearComments();
|
||||
}
|
||||
|
||||
this.artalk.request('CommentGet', {
|
||||
@@ -75,12 +65,12 @@ export default class List extends ArtalkContext {
|
||||
}, (msg, data: ListData) => {
|
||||
this.data = { ...data }
|
||||
this.artalk.ui.setGlobalError(null)
|
||||
this.importCommentsByReqObj(data.comments)
|
||||
this.reqImportComments(data.comments)
|
||||
// 查看更多按钮
|
||||
if (this.hasMoreComments) this.showReadMoreBtn()
|
||||
else this.hideReadMoreBtn()
|
||||
// 锚点跳转
|
||||
this.checkRedirectByUrlHash()
|
||||
this.artalk.checkGoToCommentByUrlHash()
|
||||
// 滚动到底部自动加载
|
||||
if (offset === 0 && this.hasMoreComments) {
|
||||
this.initScrollBottomAutoLoad()
|
||||
@@ -101,169 +91,103 @@ export default class List extends ArtalkContext {
|
||||
}
|
||||
|
||||
/** 导入评论 - 通过请求数据 */
|
||||
importCommentsByReqObj (rawData: any[]) {
|
||||
if (!Array.isArray(rawData)) { throw new Error('putCommentsByObj 出错:参数非数组') }
|
||||
|
||||
const newCommentList: Comment[] = []
|
||||
rawData.forEach((commentData) => {
|
||||
if (commentData.id === 0) {
|
||||
throw new Error('黑人问号 ??? Comment 的 ID 怎么可能是 0 ?')
|
||||
}
|
||||
if (commentData.rid === 0) {
|
||||
newCommentList.push(new Comment(this, commentData))
|
||||
}
|
||||
})
|
||||
private reqImportComments (rawData: any[]) {
|
||||
if (!Array.isArray(rawData)) { throw new Error('请求响应参数非数组') }
|
||||
|
||||
// 查找并导入所有子评论
|
||||
const queryChildren = (parentComment: Comment) => {
|
||||
rawData.forEach((commentData) => {
|
||||
if (commentData.rid === parentComment.data.id) {
|
||||
const cComment = new Comment(this, commentData)
|
||||
parentComment.setChild(cComment)
|
||||
cComment.playFadeInAnim()
|
||||
const queryImportChildren = (parentC: Comment) => {
|
||||
const children = rawData.filter(o => o.rid === parentC.data.id)
|
||||
if (children.length === 0) return
|
||||
|
||||
// 是否存在子评论
|
||||
const hasChild = (parentId: number) => {
|
||||
return rawData.find(o => o.rid === parentId) !== null
|
||||
}
|
||||
children.forEach((itemData: CommentData) => {
|
||||
itemData.is_allow_reply = parentC.data.is_allow_reply
|
||||
const childC = new Comment(this.artalk, this, itemData)
|
||||
parentC.putChild(childC)
|
||||
|
||||
// 继续 递归查找子评论
|
||||
if (hasChild(cComment.data.id)) {
|
||||
queryChildren(cComment)
|
||||
}
|
||||
}
|
||||
queryImportChildren(childC); // 递归查找所有子评论
|
||||
})
|
||||
}
|
||||
|
||||
// 查找并导入子评论
|
||||
newCommentList.forEach((comment) => {
|
||||
queryChildren(comment)
|
||||
// 开始处理 rawData
|
||||
rawData.filter((o) => o.rid === 0).forEach((rootCommentData: CommentData) => {
|
||||
if (rootCommentData.is_collapsed) rootCommentData.is_allow_reply = false
|
||||
const rootComment = new Comment(this.artalk, this, rootCommentData)
|
||||
this.artalk.comments.push(rootComment) // 将评论导入 comments 总表中
|
||||
|
||||
this.commentsWrapEl.appendChild(rootComment.getElem())
|
||||
rootComment.playFadeInAnim()
|
||||
|
||||
queryImportChildren(rootComment)
|
||||
})
|
||||
|
||||
// 将评论导入 this.comments 总表中
|
||||
this.comments.push(...newCommentList)
|
||||
|
||||
// 装载评论元素
|
||||
newCommentList.forEach((comment) => {
|
||||
this.commentsWrapEl.appendChild(comment.getElem())
|
||||
comment.playFadeInAnim()
|
||||
})
|
||||
|
||||
this.refreshUI()
|
||||
this.refreshUI(true)
|
||||
}
|
||||
|
||||
/** 添加评论项 */
|
||||
putComment (comment: Comment) {
|
||||
public putRootComment (comment: Comment) {
|
||||
this.commentsWrapEl.prepend(comment.getElem())
|
||||
this.comments.unshift(comment)
|
||||
|
||||
comment.playFadeInAnim()
|
||||
this.refreshUI()
|
||||
this.artalk.comments.unshift(comment)
|
||||
}
|
||||
|
||||
/** 查找评论项 */
|
||||
findComment (id: number) {
|
||||
let comment: Comment|null = null
|
||||
|
||||
this.eachComment(this.comments, (item) => {
|
||||
if (item.data.id === id) {
|
||||
comment = item
|
||||
return false
|
||||
}
|
||||
return true
|
||||
private initListActionBtn () {
|
||||
// 侧边栏呼出按钮
|
||||
this.openSidebarBtnEl = this.el.querySelector('[data-action="open-sidebar"]')
|
||||
this.openSidebarBtnEl.addEventListener('click', () => {
|
||||
this.artalk.sidebar.show()
|
||||
})
|
||||
|
||||
return comment
|
||||
}
|
||||
|
||||
/** 遍历操作 Comment (包括子评论) */
|
||||
eachComment (commentList: Comment[], action: (comment?: Comment, levelList?: Comment[]) => boolean|void) {
|
||||
if (commentList.length === 0) return
|
||||
commentList.every((item) => {
|
||||
if (action(item, commentList) === false) return false
|
||||
this.eachComment(item.getChildren(), action)
|
||||
return true
|
||||
// 关闭评论按钮
|
||||
this.closeCommentBtnEl = this.el.querySelector('[data-action="admin-close-comment"]') as HTMLElement
|
||||
this.closeCommentBtnEl.addEventListener('click', () => {
|
||||
this.adminSetPage({
|
||||
is_close_comment: !this.data.page.is_close_comment
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除评论 */
|
||||
deleteComment (comment: number|Comment) {
|
||||
let findComment: Comment
|
||||
if (typeof comment === 'number') {
|
||||
findComment = this.findComment(comment)
|
||||
if (!findComment) throw Error(`未找到评论 ${comment}`)
|
||||
} else findComment = comment
|
||||
|
||||
findComment.getElem().remove()
|
||||
this.eachComment(this.comments, (item, levelList) => {
|
||||
if (item === findComment) {
|
||||
levelList.splice(levelList.indexOf(item), 1)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空所有评论 */
|
||||
clearComments () {
|
||||
this.commentsWrapEl.innerHTML = ''
|
||||
this.comments = []
|
||||
this.data = undefined
|
||||
}
|
||||
|
||||
/** 刷新界面 */
|
||||
refreshUI () {
|
||||
(this.el.querySelector('.artalk-comment-count-num') as HTMLElement).innerText = this.getCommentCount().toString()
|
||||
public refreshUI (isFirstUse: boolean = false) {
|
||||
(this.el.querySelector('.artalk-comment-count-num') as HTMLElement).innerText = this.getListCommentCount().toString()
|
||||
|
||||
let noCommentElem = this.commentsWrapEl.querySelector('.artalk-no-comment') as HTMLElement
|
||||
if (this.comments.length <= 0 && !noCommentElem) {
|
||||
if (this.artalk.comments.length <= 0 && !noCommentElem) {
|
||||
noCommentElem = Utils.createElement('<div class="artalk-no-comment"></div>')
|
||||
noCommentElem.innerText = this.artalk.conf.noComment
|
||||
this.commentsWrapEl.appendChild(noCommentElem)
|
||||
}
|
||||
if (this.comments.length > 0 && noCommentElem !== null) {
|
||||
if (this.artalk.comments.length > 0 && noCommentElem !== null) {
|
||||
noCommentElem.remove()
|
||||
}
|
||||
|
||||
// 已输入个人信息
|
||||
if (!!this.artalk.user.nick && !!this.artalk.user.email) {
|
||||
this.openSidebarBtnEl.style.display = ''
|
||||
if (!!this.artalk.user.data.nick && !!this.artalk.user.data.email) {
|
||||
this.openSidebarBtnEl.classList.remove('artalk-hide')
|
||||
} else {
|
||||
this.openSidebarBtnEl.style.display = 'none'
|
||||
this.openSidebarBtnEl.classList.add('artalk-hide')
|
||||
}
|
||||
|
||||
// 是管理员
|
||||
if (this.artalk.user.isAdmin) {
|
||||
this.el.querySelectorAll('[data-only-admin-show]').forEach((itemEl: HTMLElement) => {
|
||||
// 仅管理员显示控制
|
||||
this.el.querySelectorAll('[data-list-ui-only-admin]').forEach((itemEl: HTMLElement) => {
|
||||
if (this.artalk.user.data.isAdmin)
|
||||
itemEl.classList.remove('artalk-hide')
|
||||
})
|
||||
} else {
|
||||
this.el.querySelectorAll('[data-only-admin-show]').forEach((itemEl: HTMLElement) => {
|
||||
else
|
||||
itemEl.classList.add('artalk-hide')
|
||||
})
|
||||
})
|
||||
|
||||
// 关闭评论
|
||||
if (!!this.data && !!this.data.page && this.data.page.is_close_comment === true) {
|
||||
this.artalk.editor.closeComment()
|
||||
this.closeCommentBtnEl.innerHTML = '打开评论'
|
||||
} else if (!isFirstUse) {
|
||||
this.artalk.editor.openComment()
|
||||
this.closeCommentBtnEl.innerHTML = '关闭评论'
|
||||
}
|
||||
}
|
||||
|
||||
/** 跳到评论项位置 - 根据 `location.hash` */
|
||||
checkRedirectByUrlHash () {
|
||||
let commentId: number = Number(Utils.getLocationParmByName('artalk_comment'))
|
||||
if (!commentId) {
|
||||
const match = window.location.hash.match(/#artalk-comment-([0-9]+)/)
|
||||
if (!match || !match[1] || Number.isNaN(Number(match[1]))) return
|
||||
commentId = Number(match[1])
|
||||
}
|
||||
|
||||
const comment = this.findComment(commentId)
|
||||
if (!comment && this.hasMoreComments) {
|
||||
this.readMore()
|
||||
return
|
||||
}
|
||||
if (!comment) { return }
|
||||
|
||||
this.artalk.ui.scrollIntoView(comment.getElem(), false)
|
||||
setTimeout(() => {
|
||||
comment.getElem().classList.add('artalk-flash-once')
|
||||
}, 800)
|
||||
/** 获取评论总数 (包括子评论) */
|
||||
public getListCommentCount (): number {
|
||||
if (!this.data || !this.data.total) return 0
|
||||
return Number(this.data.total || '0')
|
||||
}
|
||||
|
||||
/** 是否还有更多的评论 */
|
||||
@@ -326,19 +250,32 @@ export default class List extends ArtalkContext {
|
||||
}
|
||||
|
||||
/** 根据请求数据判断 nick 是否为管理员 */
|
||||
checkNickEmailIsAdmin (nick: string, email: string) {
|
||||
public checkNickEmailIsAdmin (nick: string, email: string) {
|
||||
if (!this.data || !this.data.admin_nicks || !this.data.admin_encrypted_emails) return false
|
||||
|
||||
return (this.data.admin_nicks.indexOf(nick) !== -1)
|
||||
&& (this.data.admin_encrypted_emails.find(o => String(o).toLowerCase() === String(md5(email)).toLowerCase()))
|
||||
}
|
||||
|
||||
/** 获取评论总数 (包括子评论) */
|
||||
getCommentCount (): number {
|
||||
let count = 0
|
||||
this.eachComment(this.comments, () => {
|
||||
count++
|
||||
/** 管理员设置页面信息 */
|
||||
public adminSetPage (conf: {
|
||||
is_close_comment: boolean
|
||||
}) {
|
||||
this.artalk.request('SetPage', {
|
||||
nick: this.artalk.user.data.nick,
|
||||
email: this.artalk.user.data.email,
|
||||
page_key: this.artalk.conf.pageKey,
|
||||
password: this.artalk.user.data.password,
|
||||
is_close_comment: Number(conf.is_close_comment)
|
||||
}, () => {
|
||||
this.artalk.ui.showLoading(this.artalk.editor.el)
|
||||
}, () => {
|
||||
this.artalk.ui.hideLoading(this.artalk.editor.el)
|
||||
}, (msg, data) => {
|
||||
this.data.page = { ...data }
|
||||
this.refreshUI()
|
||||
}, (msg, data) => {
|
||||
this.artalk.editor.showNotify(`修改页面数据失败:${msg}`, 'e')
|
||||
})
|
||||
return count
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
import Comment from './Comment'
|
||||
import '../css/sidebar.less'
|
||||
import Artalk from '../Artalk'
|
||||
import ArtalkContext from '../ArtalkContext'
|
||||
import Utils from '../utils'
|
||||
import { CommentData } from '~/types/artalk-data'
|
||||
import Layer from './Layer'
|
||||
import BuildLayer, { Layer } from './Layer'
|
||||
|
||||
export default class Sidebar extends ArtalkContext {
|
||||
public el: HTMLElement
|
||||
public layer: Layer
|
||||
public contentEl: HTMLElement
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
constructor (artalk: Artalk) {
|
||||
super(artalk)
|
||||
|
||||
this.el = Utils.createElement(require('../templates/Sidebar.ejs')(this))
|
||||
this.layer = new Layer('sidebar', this.el)
|
||||
this.contentEl = this.el.querySelector('.artalk-sidebar-content')
|
||||
|
||||
this.el.querySelector('.artalk-sidebar-close').addEventListener('click', () => {
|
||||
@@ -23,6 +23,7 @@ export default class Sidebar extends ArtalkContext {
|
||||
}
|
||||
|
||||
show () {
|
||||
this.layer = BuildLayer(this.artalk, 'sidebar', this.el)
|
||||
this.layer.show()
|
||||
this.contentEl.scrollTo(0, 0)
|
||||
setTimeout(() => {
|
||||
@@ -30,8 +31,8 @@ export default class Sidebar extends ArtalkContext {
|
||||
}, 20)
|
||||
|
||||
this.artalk.request('CommentReplyGet', {
|
||||
nick: this.artalk.user.nick,
|
||||
email: this.artalk.user.email
|
||||
nick: this.artalk.user.data.nick,
|
||||
email: this.artalk.user.data.email
|
||||
}, () => {
|
||||
this.artalk.ui.showLoading(this.contentEl)
|
||||
}, () => {
|
||||
@@ -50,11 +51,11 @@ export default class Sidebar extends ArtalkContext {
|
||||
|
||||
hide () {
|
||||
this.el.style.transform = ''
|
||||
this.layer.hide()
|
||||
this.layer.dispose() // 用完即销毁
|
||||
}
|
||||
|
||||
putComment (data: CommentData) {
|
||||
const comment = new Comment(null, data)
|
||||
const comment = new Comment(this.artalk, null, data)
|
||||
|
||||
comment.elem.querySelector('[data-comment-action="reply"]').remove()
|
||||
comment.elem.style.cursor = 'pointer'
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import Artalk from '../Artalk';
|
||||
import ArtalkContext from '../ArtalkContext';
|
||||
|
||||
export default class User extends ArtalkContext {
|
||||
public data: {
|
||||
nick: string|null,
|
||||
email: string|null,
|
||||
link: string|null,
|
||||
password: string|null,
|
||||
isAdmin: boolean
|
||||
}
|
||||
|
||||
constructor (artalk: Artalk) {
|
||||
super(artalk)
|
||||
|
||||
// 从 localStorage 导入
|
||||
const localUser = JSON.parse(window.localStorage.getItem('ArtalkUser') || '{}')
|
||||
this.data = {
|
||||
nick: localUser.nick || '',
|
||||
email: localUser.email || '',
|
||||
link: localUser.link || '',
|
||||
password: localUser.password || '',
|
||||
isAdmin: localUser.isAdmin || false
|
||||
}
|
||||
}
|
||||
|
||||
/** 保存用户到 localStorage 中 */
|
||||
public save () {
|
||||
window.localStorage.setItem('ArtalkUser', JSON.stringify(this.data))
|
||||
}
|
||||
|
||||
/** 是否已填写基本用户信息 */
|
||||
public checkHasBasicUserInfo () {
|
||||
return !!this.data.nick && !!this.data.email
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,23 @@ import Editor from '../Editor'
|
||||
import ArtalkContext from '~/src/ArtalkContext'
|
||||
import Utils from '~/src/utils'
|
||||
|
||||
type EmoticonListType = {
|
||||
[grpName: string]: {
|
||||
inputType: 'emoticon'|'image'
|
||||
container: { [name: string]: string }
|
||||
}
|
||||
}
|
||||
|
||||
export default class EmoticonsPlug extends ArtalkContext {
|
||||
public elem: HTMLElement
|
||||
public emoticons: any
|
||||
public emoticons: EmoticonListType
|
||||
|
||||
public listWrapElem: HTMLElement
|
||||
public typesElem: HTMLElement
|
||||
|
||||
|
||||
constructor (public editor: Editor) {
|
||||
super()
|
||||
super(editor.artalk)
|
||||
|
||||
this.emoticons = this.artalk.conf.emoticons
|
||||
this.initElem()
|
||||
@@ -46,7 +53,7 @@ export default class EmoticonsPlug extends ArtalkContext {
|
||||
const title = elem.getAttribute('title')
|
||||
const content = elem.getAttribute('data-content')
|
||||
if (inputType === 'image') {
|
||||
this.editor.insertContent(``)
|
||||
this.editor.insertContent(`:[${title}]`)
|
||||
} else {
|
||||
this.editor.insertContent(content)
|
||||
}
|
||||
@@ -82,8 +89,8 @@ export default class EmoticonsPlug extends ArtalkContext {
|
||||
}
|
||||
|
||||
changeListHeight () {
|
||||
const listWrapHeight = Utils.getHeight(this.listWrapElem)
|
||||
this.editor.plugWrapEl.style.height = `${listWrapHeight > 150 ? listWrapHeight : 150}px`
|
||||
/* const listWrapHeight = Utils.getHeight(this.listWrapElem)
|
||||
this.editor.plugWrapEl.style.height = `${listWrapHeight > 150 ? listWrapHeight : 150}px` */
|
||||
}
|
||||
|
||||
onShow () {
|
||||
@@ -96,4 +103,15 @@ export default class EmoticonsPlug extends ArtalkContext {
|
||||
onHide () {
|
||||
this.elem.parentElement.style.height = ''
|
||||
}
|
||||
|
||||
public transEmoticonImageText (text: string) {
|
||||
Object.entries(this.emoticons).forEach(([grpName, grp]) => {
|
||||
if (grp.inputType !== 'image') return
|
||||
Object.entries(grp.container).forEach(([name, imgSrc]) => {
|
||||
text = text.split(`:[${name}]`).join(` `) // replaceAll(...)
|
||||
})
|
||||
})
|
||||
|
||||
return text
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@ export default class PreviewPlug extends ArtalkContext {
|
||||
public elem: HTMLElement
|
||||
|
||||
constructor (public editor: Editor) {
|
||||
super()
|
||||
super(editor.artalk)
|
||||
|
||||
this.initElem()
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export default class PreviewPlug extends ArtalkContext {
|
||||
public binded: boolean = false
|
||||
|
||||
constructor (public editor: Editor) {
|
||||
super()
|
||||
super(editor.artalk)
|
||||
|
||||
this.initElem()
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
width: 100%;
|
||||
|
||||
.artalk-emoticons-list-wrap {
|
||||
/* overflow-y: scroll;
|
||||
.artalk-slim-scrollbar();
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
height: calc(100% - 30px); */
|
||||
height: 100%; // calc(100% - 30px);
|
||||
width: 100%;
|
||||
|
||||
.artalk-emoticons-list {
|
||||
@@ -25,7 +26,7 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #F4F4F4;
|
||||
background: @color-bg-grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +52,7 @@
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background: #F4F4F4;
|
||||
background: @color-bg-grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
.artalk-slim-scrollbar() {
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb,
|
||||
&::-webkit-scrollbar-thumb:window-inactive {
|
||||
background: rgba(86,86,86,.3);
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:vertical:hover {
|
||||
background: rgba(65,74,82,.77);
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:vertical:active {
|
||||
background: rgba(41,47,53,.77);
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,16 @@
|
||||
@color-meta: #757e91;
|
||||
@color-border: #eceff2;
|
||||
@color-bg: #FFF;
|
||||
@color-bg-grey: #F4F4F4;
|
||||
@color-main: #0083ff;
|
||||
@color-red: #ff5652;
|
||||
@color-yellow: #ffa928;
|
||||
|
||||
@font-family: "Helvetica Neue", "PingFangSC-Light", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
|
||||
@font-size: 14px;
|
||||
@line-height: 1.6em;
|
||||
|
||||
@block-margin: 15px;
|
||||
@block-margin-mobile: 7px;
|
||||
@block-padding: 10px;
|
||||
@block-radius: 4px;
|
||||
+83
-30
@@ -1,7 +1,6 @@
|
||||
.artalk-comment-wrap {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid @color-border;
|
||||
|
||||
&.artalk-flash-once {
|
||||
@@ -38,7 +37,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
& > .artalk-content {
|
||||
& > .artalk-comment-main {
|
||||
display: block;
|
||||
flex: auto;
|
||||
|
||||
@@ -53,7 +52,7 @@
|
||||
align-items: end;
|
||||
|
||||
.badge() {
|
||||
background: #F4F4F4;
|
||||
background: @color-bg-grey;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
@@ -93,17 +92,67 @@
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
& > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
& > .artalk-content {
|
||||
&.artalk-type-collapsed {
|
||||
border: 3px solid @color-bg-grey;
|
||||
border-bottom: 0;
|
||||
padding: 5px 10px;
|
||||
border-radius: @block-radius @block-radius 0 0;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
&.artalk-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& > .artalk-collapsed {
|
||||
margin: 3px 0;
|
||||
font-size: 13px;
|
||||
padding: 10px 18px;
|
||||
display: block;
|
||||
background: @color-bg-grey;
|
||||
border-radius: @block-radius;
|
||||
|
||||
& > .artalk-text {
|
||||
color: @color-meta;
|
||||
}
|
||||
|
||||
& > .artalk-show-btn {
|
||||
color: @color-main;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
margin-left: 3px;
|
||||
&:hover {
|
||||
color: @color-main;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.artalk-read-more-mask:after {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
content: " ";
|
||||
bottom: 0;
|
||||
height: 41px;
|
||||
background: linear-gradient(180deg, hsla(0,0%,100%,0), hsla(0,0%,100%,1));
|
||||
}
|
||||
}
|
||||
|
||||
& > .artalk-footer {
|
||||
@@ -141,29 +190,33 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .artalk-comment-children {
|
||||
margin-top: 10px;
|
||||
border-radius: 3px;
|
||||
border-left: 1px dashed hsla(0,0%,93%,.5);
|
||||
.comment-small-size() {
|
||||
border-bottom-color: transparent;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.artalk-comment-wrap {
|
||||
border-bottom-color: transparent;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
& > .artalk-comment {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
& > .artalk-comment {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
& > .artalk-avatar img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
& > .artalk-avatar img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.artalk-comment-children {
|
||||
.artalk-comment-wrap {
|
||||
margin-top: 10px;
|
||||
border-radius: 3px;
|
||||
border-left: 1px dashed hsla(0,0%,93%,.5);
|
||||
|
||||
.comment-small-size()
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
-1
@@ -4,6 +4,13 @@
|
||||
margin-bottom: @block-margin;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #FFF;
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
& > {
|
||||
margin-bottom: @block-margin-mobile;
|
||||
}
|
||||
}
|
||||
|
||||
& > .artalk-editor-header {
|
||||
display: flex;
|
||||
@@ -41,6 +48,32 @@
|
||||
border: 2px solid transparent;
|
||||
border-radius: @block-radius/2;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.artalk-close-comment {
|
||||
display: flex;
|
||||
user-select: none;
|
||||
// background: repeating-linear-gradient(145deg, #f2f2f2, #f2f2f2 15px, #fff 0, #fff 30px);
|
||||
padding: 0;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
min-height: 30px;
|
||||
border-top: 1px solid @color-border;
|
||||
margin-top: 4px;
|
||||
font-weight: 100;
|
||||
font-size: 12px;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
place-content: flex-end;
|
||||
flex-direction: row;
|
||||
background: #FFF;
|
||||
|
||||
& > span {
|
||||
color: @color-meta;
|
||||
border-radius: 2px;
|
||||
margin-right: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.artalk-send-reply {
|
||||
@@ -84,7 +117,7 @@
|
||||
|
||||
& > .artalk-editor-plug-wrap {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
height: 180px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border-top: 1px solid @color-border;
|
||||
|
||||
+15
-1
@@ -26,9 +26,23 @@
|
||||
|
||||
& > span {
|
||||
font-size: 14px;
|
||||
color: @color-main;
|
||||
color: @color-meta;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&.artalk-on {
|
||||
color: @color-main;
|
||||
}
|
||||
|
||||
&.artalk-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:not(:last-child):not(.artalk-hide) {
|
||||
margin-right: 10px;
|
||||
padding-right: 10px;
|
||||
border-right: 1px solid @color-border;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -11,6 +11,7 @@
|
||||
color: @color-default;
|
||||
font-family: @font-family;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
pre {
|
||||
@@ -48,6 +49,10 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.full-layer {
|
||||
@@ -123,21 +128,18 @@
|
||||
|
||||
@keyframes artalkLoadingIconRotate {
|
||||
0% { transform: rotate(0) }
|
||||
50% { transform: rotate(180deg) }
|
||||
100% { transform: rotate(360deg) }
|
||||
}
|
||||
|
||||
.artalk-loading-icon {
|
||||
border-radius: 100%;
|
||||
margin: 0;
|
||||
border: 1px solid @color-main;
|
||||
border-bottom-color: transparent;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
background: 0 0 !important;
|
||||
display: inline-block;
|
||||
animation: artalkLoadingIconRotate .75s 0s linear infinite;
|
||||
vertical-align: middle;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
box-sizing: border-box;
|
||||
border: solid 1px transparent;
|
||||
border-top-color: #29d;
|
||||
border-left-color: #29d;
|
||||
border-radius: 50%;
|
||||
animation: artalkLoadingIconRotate .4s linear infinite;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@@ -174,12 +176,12 @@
|
||||
}
|
||||
|
||||
.artalk-layer-dialog-wrap {
|
||||
@formInputHeight: 25px;
|
||||
@formInputHeight: 30px;
|
||||
&:extend(.full-layer);
|
||||
background-color: rgba(255, 255, 255, 0.94);
|
||||
|
||||
& > .artalk-layer-dialog {
|
||||
width: 30%;
|
||||
width: 25%;
|
||||
|
||||
& > .artalk-layer-dialog-content {
|
||||
.artalk-captcha-img {
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
transition: transform 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;
|
||||
transform: translate(@width, 0px);
|
||||
|
||||
@media only screen and (max-width: @width) {
|
||||
& {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.artalk-sidebar-inner {
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -5,23 +5,36 @@
|
||||
<img src="<%= getGravatarUrl() %>">
|
||||
</a>
|
||||
</div>
|
||||
<div class="artalk-content">
|
||||
<div class="artalk-comment-main">
|
||||
<div class="artalk-header">
|
||||
<span class="artalk-nick">
|
||||
<a target="_blank" <% if(!!data.link){ %>href="<%= data.link %>"<% } %>><%= data.nick %></a>
|
||||
</span>
|
||||
<% if (!!data.badge) { %>
|
||||
<span class="artalk-badge"><%= data.badge %></span>
|
||||
<span class="artalk-badge" <% if(!!data.badge.color){ %>style="background-color: <%= data.badge.color %>"<% } %>>
|
||||
<%= data.badge.name || '管理员' %>
|
||||
</span>
|
||||
<% } %>
|
||||
<span class="artalk-date"><%= timeAgo(new Date(data.date)) %></span>
|
||||
<span class="artalk-date"><%= getDateFormatted() %></span>
|
||||
<span class="artalk-ua"><%= getUserUaBrowser() %></span>
|
||||
<span class="artalk-ua"><%= getUserUaOS() %></span>
|
||||
</div>
|
||||
<div class="artalk-body"><%- getContentMarked() %></div>
|
||||
<div class="artalk-body">
|
||||
<div class="artalk-content<%= !!data.is_collapsed ? ' artalk-hide artalk-type-collapsed' : '' %>"><%- getContentMarked() %></div>
|
||||
<% if(!!data.is_collapsed) { %>
|
||||
<div class="artalk-collapsed">
|
||||
<span class="artalk-text">该评论已被系统或管理员折叠</span>
|
||||
<span class="artalk-show-btn">查看内容</span>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="artalk-footer">
|
||||
<div class="artalk-comment-actions">
|
||||
<span data-comment-action="reply">回复</span>
|
||||
<span data-comment-action="delete" class="artalk-hide" data-only-admin-show>删除</span>
|
||||
<% if(data.is_allow_reply !== false) { %><span data-comment-action="reply">回复</span><% } %>
|
||||
<span data-comment-action="collapse" class="artalk-hide" data-list-ui-only-admin>
|
||||
<%= !!data.is_collapsed ? '取消折叠' : '折叠' %>
|
||||
</span>
|
||||
<span data-comment-action="delete" class="artalk-hide" data-list-ui-only-admin>删除</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
<input name="link" placeholder="网址 (https://)" class="artalk-link" type="url">
|
||||
</div>
|
||||
<div class="artalk-editor-textarea-wrap">
|
||||
<div class="artalk-close-comment" style="display: none;"><span>仅管理员可评论</span></div>
|
||||
<textarea id="artalk-editor-textarea" class="artalk-editor-textarea" placeholder="<%= artalk.conf.placeholder %>"></textarea>
|
||||
</div>
|
||||
<div class="artalk-editor-plug-wrap" style="display: none;"></div>
|
||||
<div class="artalk-editor-bottom">
|
||||
<div class="artalk-editor-bottom-part artalk-left artalk-editor-plug-switcher-wrap"></div>
|
||||
<div class="artalk-editor-bottom-part artalk-right">
|
||||
<button type="button" class="artalk-send-btn">发送评论</button>
|
||||
<button type="button" class="artalk-send-btn"><%= artalk.conf.sendBtn %></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="artalk-editor-notify-wrap"></div>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
条评论
|
||||
</div>
|
||||
<div class="artalk-right-action">
|
||||
<span data-action="open-sidebar" style="display: none;">通知中心</span>
|
||||
<span data-action="admin-close-comment" class="artalk-hide" data-list-ui-only-admin>关闭评论</span>
|
||||
<span data-action="open-sidebar" class="artalk-hide artalk-on">通知中心</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="artalk-list-body">
|
||||
|
||||
@@ -37,4 +37,60 @@ export default class Utils {
|
||||
left: rect.left + window.scrollX
|
||||
}
|
||||
}
|
||||
|
||||
public static timeAgo (date: Date) {
|
||||
try {
|
||||
const oldTime = date.getTime()
|
||||
const currTime = new Date().getTime()
|
||||
const diffValue = currTime - oldTime
|
||||
|
||||
const days = Math.floor(diffValue / (24 * 3600 * 1000))
|
||||
if (days === 0) {
|
||||
// 计算相差小时数
|
||||
const leave1 = diffValue % (24 * 3600 * 1000) // 计算天数后剩余的毫秒数
|
||||
const hours = Math.floor(leave1 / (3600 * 1000))
|
||||
if (hours === 0) {
|
||||
// 计算相差分钟数
|
||||
const leave2 = leave1 % (3600 * 1000) // 计算小时数后剩余的毫秒数
|
||||
const minutes = Math.floor(leave2 / (60 * 1000))
|
||||
if (minutes === 0) {
|
||||
// 计算相差秒数
|
||||
const leave3 = leave2 % (60 * 1000) // 计算分钟数后剩余的毫秒数
|
||||
const seconds = Math.round(leave3 / 1000)
|
||||
return `${seconds} 秒前`
|
||||
}
|
||||
return `${minutes} 分钟前`
|
||||
}
|
||||
return `${hours} 小时前`
|
||||
}
|
||||
if (days < 0) return '刚刚'
|
||||
|
||||
if (days < 8) {
|
||||
return `${days} 天前`
|
||||
}
|
||||
|
||||
return this.dateFormat(date)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return ' - '
|
||||
}
|
||||
}
|
||||
|
||||
public static padWithZeros (vNumber: number, width: number) {
|
||||
let numAsString = vNumber.toString()
|
||||
while (numAsString.length < width) {
|
||||
numAsString = `0${numAsString}`
|
||||
}
|
||||
return numAsString
|
||||
}
|
||||
|
||||
public static dateFormat (date: Date) {
|
||||
const vDay = this.padWithZeros(date.getDate(), 2)
|
||||
const vMonth = this.padWithZeros(date.getMonth() + 1, 2)
|
||||
const vYear = this.padWithZeros(date.getFullYear(), 2)
|
||||
// var vHour = padWithZeros(date.getHours(), 2);
|
||||
// var vMinute = padWithZeros(date.getMinutes(), 2);
|
||||
// var vSecond = padWithZeros(date.getSeconds(), 2);
|
||||
return `${vYear}-${vMonth}-${vDay}`
|
||||
}
|
||||
}
|
||||
+24
-2
@@ -1,11 +1,12 @@
|
||||
import Artalk from '../Artalk'
|
||||
import ArtalkContext from '../ArtalkContext'
|
||||
import Utils from '.'
|
||||
|
||||
export default class Ui extends ArtalkContext {
|
||||
public el: HTMLElement
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
constructor (artalk: Artalk) {
|
||||
super(artalk)
|
||||
this.el = this.artalk.el
|
||||
}
|
||||
|
||||
@@ -152,4 +153,25 @@ export default class Ui extends ArtalkContext {
|
||||
|
||||
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop))
|
||||
}
|
||||
|
||||
playFadeAnim (elem: HTMLElement, after?: () => void, type: 'in'|'out' = 'in') {
|
||||
elem.classList.add(`artalk-fade-${type}`)
|
||||
// 动画结束清除 class
|
||||
const onAnimEnded = () => {
|
||||
elem.classList.remove(`artalk-fade-${type}`)
|
||||
elem.removeEventListener('animationend', onAnimEnded)
|
||||
if (after) after()
|
||||
}
|
||||
elem.addEventListener('animationend', onAnimEnded)
|
||||
}
|
||||
|
||||
/** 播放渐入动画 */
|
||||
playFadeInAnim (elem: HTMLElement, after?: () => void) {
|
||||
this.playFadeAnim(elem, after, 'in')
|
||||
}
|
||||
|
||||
/** 播放渐出动画 */
|
||||
playFadeOutAnim (elem: HTMLElement, after?: () => void) {
|
||||
this.playFadeAnim(elem, after, 'out')
|
||||
}
|
||||
}
|
||||
Vendored
+5
@@ -14,6 +14,11 @@ export interface ArtalkConfig {
|
||||
*/
|
||||
noComment?: string
|
||||
|
||||
/**
|
||||
* 发送按钮文字
|
||||
*/
|
||||
sendBtn?: string
|
||||
|
||||
/**
|
||||
* 默认头像(URL or Gravatar Type)
|
||||
*/
|
||||
|
||||
Vendored
+22
@@ -25,6 +25,21 @@ export interface CommentData {
|
||||
|
||||
/** 评论日期 */
|
||||
date: string
|
||||
|
||||
/** 是否折叠 */
|
||||
is_collapsed?: boolean
|
||||
|
||||
/** 是否为管理员 */
|
||||
is_admin?: boolean
|
||||
|
||||
/** 徽章 */
|
||||
badge?: {
|
||||
name?: string
|
||||
color?: string
|
||||
}
|
||||
|
||||
/** 是否允许回复 */
|
||||
is_allow_reply?: boolean
|
||||
}
|
||||
|
||||
export interface ListData {
|
||||
@@ -48,4 +63,11 @@ export interface ListData {
|
||||
|
||||
/** 管理员加密后的邮箱 */
|
||||
admin_encrypted_emails: string[]
|
||||
|
||||
/** 页面信息 */
|
||||
page: {
|
||||
id: number
|
||||
page_key: string
|
||||
is_close_comment: boolean
|
||||
}
|
||||
}
|
||||
+10
-6
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable global-require */
|
||||
const webpack = require('webpack')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin')
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
@@ -34,8 +35,9 @@ module.exports = (env, argv) => {
|
||||
filename: '[name].js',
|
||||
library: '[name]',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default',
|
||||
umdNamedDefine: true
|
||||
libraryExport: '',
|
||||
umdNamedDefine: true,
|
||||
globalObject: 'this'
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
@@ -43,8 +45,10 @@ module.exports = (env, argv) => {
|
||||
filename: `${ROOT_PATH}/index.html`,
|
||||
template: `${ROOT_PATH}/index-tpl.ejs`,
|
||||
inject: 'head',
|
||||
hash: true
|
||||
hash: true,
|
||||
alwaysWriteToDisk: true
|
||||
}),
|
||||
new HtmlWebpackHarddiskPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
ARTALK_VERSION: `"${VERSION}"`,
|
||||
'process.env.NODE_ENV': JSON.stringify(NODE_ENV)
|
||||
@@ -108,7 +112,7 @@ module.exports = (env, argv) => {
|
||||
lessOptions: {
|
||||
paths: [SRC_PATH]
|
||||
},
|
||||
prependData: '@import "css/_variables.less";'
|
||||
prependData: '@import "css/_variables.less";@import "css/_extend.less";'
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -142,8 +146,8 @@ module.exports = (env, argv) => {
|
||||
open: true,
|
||||
hot: true,
|
||||
inline: true,
|
||||
publicPath: '/dist/',
|
||||
compress: true,
|
||||
publicPath: '/dist/', // 打包文件生成路径(内存中)
|
||||
contentBase: path.join(__dirname, '/'),
|
||||
stats: 'errors-only',
|
||||
overlay: {
|
||||
errors: true,
|
||||
|
||||
@@ -3719,6 +3719,13 @@ html-minifier@^3.2.3:
|
||||
relateurl "0.2.x"
|
||||
uglify-js "3.4.x"
|
||||
|
||||
html-webpack-harddisk-plugin@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/html-webpack-harddisk-plugin/-/html-webpack-harddisk-plugin-1.0.1.tgz#4fc034d3440ec2a223dbe8fa1a5a2eb905e86fa5"
|
||||
integrity sha512-GWfutTqfxOe53qEKocqAFrrLVP/EJbmJZDAS5jBBmALkfyc2aakoQkSgo3fitYJORWcN0Fi8ekuySxfffhhRYw==
|
||||
dependencies:
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
html-webpack-plugin@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b"
|
||||
|
||||
Reference in new issue
Block a user