diff --git a/resources/assets/js/components/Flow.vue b/resources/assets/js/components/Flow.vue index 43a851cc..1b1ac2c0 100644 --- a/resources/assets/js/components/Flow.vue +++ b/resources/assets/js/components/Flow.vue @@ -114,7 +114,9 @@ language = 'zh' break; } - this.url = $A.originUrl('js/grapheditor/' + (this.readOnly ? 'viewer' : 'index') + '.html?lang=' + language); + let route = this.readOnly ? 'viewer' : 'index'; + let theme = $A.dark.isDarkEnabled() ? 'dark' : 'light' + this.url = $A.originUrl('js/grapheditor/' + route + '.html?lang=' + language + '&theme=' + theme); }, mounted() { window.addEventListener('message', this.handleMessage) diff --git a/resources/assets/js/components/OnlyOffice.vue b/resources/assets/js/components/OnlyOffice.vue index 5c2bdbf9..2731efe9 100644 --- a/resources/assets/js/components/OnlyOffice.vue +++ b/resources/assets/js/components/OnlyOffice.vue @@ -159,7 +159,7 @@ export default { "name": this.userInfo.nickname }, "customization": { - "uiTheme": "theme-classic-light", + "uiTheme": $A.dark.isDarkEnabled() ? "theme-dark" : "theme-classic-light", }, "callbackUrl": 'http://nginx/api/file/content/office?id=' + fileKey + '&token=' + this.userToken, } diff --git a/resources/assets/js/components/TEditor.vue b/resources/assets/js/components/TEditor.vue index 0ed48b25..37badff3 100755 --- a/resources/assets/js/components/TEditor.vue +++ b/resources/assets/js/components/TEditor.vue @@ -250,6 +250,7 @@ resize: !isFull, convert_urls:false, toolbar_mode: 'sliding', + content_css: $A.dark.isDarkEnabled() ? 'dark' : 'default', setup: (editor) => { editor.ui.registry.addMenuButton('uploadImages', { text: this.$L('图片'), diff --git a/resources/assets/js/functions/web.js b/resources/assets/js/functions/web.js index d08c8ebc..437b7a7c 100755 --- a/resources/assets/js/functions/web.js +++ b/resources/assets/js/functions/web.js @@ -502,5 +502,176 @@ }, }); + /** + * ============================================================================= + * ********************************** dark ********************************* + * ============================================================================= + */ + + $.extend({ + dark: { + utils: { + filter: '-webkit-filter: url(#dark-mode-filter) !important; filter: url(#dark-mode-filter) !important;', + reverseFilter: '-webkit-filter: url(#dark-mode-reverse-filter) !important; filter: url(#dark-mode-reverse-filter) !important;', + noneFilter: '-webkit-filter: none !important; filter: none !important;', + + addExtraStyle() { + try { + return ''; + } catch (e) { + return ''; + } + }, + + addStyle(id, tag, css) { + tag = tag || 'style'; + let doc = document, styleDom = doc.getElementById(id); + if (styleDom) return; + let style = doc.createElement(tag); + style.rel = 'stylesheet'; + style.id = id; + tag === 'style' ? style.innerHTML = css : style.href = css; + document.head.appendChild(style); + }, + + getClassList(node) { + return node.classList || []; + }, + + addClass(node, name) { + this.getClassList(node).add(name); + return this; + }, + + removeClass(node, name) { + this.getClassList(node).remove(name); + return this; + }, + + hasClass(node, name) { + return this.getClassList(node).contains(name); + }, + + hasElementById(eleId) { + return document.getElementById(eleId); + }, + + removeElementById(eleId) { + let ele = document.getElementById(eleId); + ele && ele.parentNode.removeChild(ele); + }, + }, + + createDarkFilter() { + if (this.utils.hasElementById('dark-mode-svg')) return; + let svgDom = ''; + let div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); + div.innerHTML = svgDom; + let frag = document.createDocumentFragment(); + while (div.firstChild) + frag.appendChild(div.firstChild); + document.head.appendChild(frag); + }, + + createDarkStyle() { + this.utils.addStyle('dark-mode-style', 'style', ` + @media screen { + html { + ${this.utils.filter} + } + + /* Default Reverse rule */ + img, + video, + iframe, + canvas, + :not(object):not(body) > embed, + object, + svg image, + [style*="background:url"], + [style*="background-image:url"], + [style*="background: url"], + [style*="background-image: url"], + [background], + twitterwidget, + .sr-reader, + .no-dark-mode, + .sr-backdrop { + ${this.utils.reverseFilter} + } + + [style*="background:url"] *, + [style*="background-image:url"] *, + [style*="background: url"] *, + [style*="background-image: url"] *, + input, + [background] *, + twitterwidget .NaturalImage-image { + ${this.utils.noneFilter} + } + + /* Text contrast */ + html { + text-shadow: 0 0 0 !important; + } + + /* Full screen */ + .no-filter, + :-webkit-full-screen, + :-webkit-full-screen *, + :-moz-full-screen, + :-moz-full-screen *, + :fullscreen, + :fullscreen * { + ${this.utils.noneFilter} + } + + /* Page background */ + html { + background: #fff !important; + } + ${this.utils.addExtraStyle()} + } + + @media print { + .no-print { + display: none !important; + } + }`); + }, + + enableDarkMode() { + if (this.isDarkEnabled()) { + return + } + this.createDarkFilter(); + this.createDarkStyle(); + this.utils.addClass(document.body, "dark-mode-reverse") + }, + + disableDarkMode() { + if (!this.isDarkEnabled()) { + return + } + this.utils.removeElementById('dark-mode-svg'); + this.utils.removeElementById('dark-mode-style'); + this.utils.removeClass(document.body, "dark-mode-reverse") + }, + + autoDarkMode() { + let darkScheme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches + if (darkScheme) { + this.enableDarkMode() + } else { + this.disableDarkMode() + } + }, + + isDarkEnabled() { + return this.utils.hasClass(document.body, "dark-mode-reverse") + }, + } + }); + window.$A = $; })(window); diff --git a/resources/assets/js/pages/manage.vue b/resources/assets/js/pages/manage.vue index b53760a7..30c4ef52 100644 --- a/resources/assets/js/pages/manage.vue +++ b/resources/assets/js/pages/manage.vue @@ -22,6 +22,17 @@ :key="key" :divided="!!item.divided" :name="item.path">{{$L(item.name)}} + + +
+ {{$L('主题皮肤')}} + +
+
+ + {{$L(item.name)}} + +
@@ -302,7 +313,10 @@ export default { 'cacheProjects', 'projectTotal', 'taskId', - 'wsOpenNum' + 'wsOpenNum', + + 'themeMode', + 'themeList' ]), ...mapGetters(['taskData', 'dashboardTask']), @@ -454,6 +468,22 @@ export default { } }, + setTheme(mode) { + switch (mode) { + case 'dark': + $A.dark.enableDarkMode() + break; + case 'light': + $A.dark.disableDarkMode() + break; + default: + $A.dark.autoDarkMode() + break; + } + this.$store.state.themeMode = mode; + $A.setStorage("cacheThemeMode", mode); + }, + toggleRoute(path) { this.show768Menu = false; this.goForward({path: '/manage/' + path}); diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index c6a9b98f..43afb132 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -386,6 +386,7 @@ export default { return new Promise(function (resolve) { try { const cacheLoginEmail = $A.getStorageString("cacheLoginEmail"); + const cacheThemeMode = $A.getStorageString("cacheThemeMode"); // window.localStorage.clear(); // @@ -398,6 +399,7 @@ export default { $A.setStorage("cacheProjectParameter", state.cacheProjectParameter); $A.setStorage("cacheServerUrl", state.cacheServerUrl); $A.setStorage("cacheLoginEmail", cacheLoginEmail); + $A.setStorage("cacheThemeMode", cacheThemeMode); dispatch("saveUserInfo", $A.isJson(userInfo) ? userInfo : state.userInfo); // resolve() diff --git a/resources/assets/js/store/state.js b/resources/assets/js/store/state.js index aac7e148..96205031 100644 --- a/resources/assets/js/store/state.js +++ b/resources/assets/js/store/state.js @@ -107,4 +107,23 @@ state.taskColorList = [ {name: '灰色', color: '#f3f3f3'}, ]; +// 主题皮肤 +state.themeMode = $A.getStorageString("cacheThemeMode", "auto"); +state.themeList = [ + {name: '跟随系统', value: 'auto'}, + {name: '明亮', value: 'light'}, + {name: '暗黑', value: 'dark'}, +]; +switch (state.themeMode) { + case 'dark': + $A.dark.enableDarkMode() + break; + case 'light': + $A.dark.disableDarkMode() + break; + default: + $A.dark.autoDarkMode() + break; +} + export default state diff --git a/resources/assets/sass/app.scss b/resources/assets/sass/app.scss index 6c2d95dc..63b8ed40 100644 --- a/resources/assets/sass/app.scss +++ b/resources/assets/sass/app.scss @@ -8,3 +8,5 @@ @import "components/_"; @import "pages/_"; +@import "dark"; + diff --git a/resources/assets/sass/dark.scss b/resources/assets/sass/dark.scss new file mode 100644 index 00000000..d73f6255 --- /dev/null +++ b/resources/assets/sass/dark.scss @@ -0,0 +1,157 @@ +body.dark-mode-reverse { + .ivu-btn-primary, + .ivu-badge-count, + .common-app-down-link { + color: #000; + } + + .ivu-select-dropdown { + box-shadow: rgba(255, 255, 255, 0.2) 0 1px 6px; + } + + .ivu-modal-mask { + background-color: rgba(230, 230, 230, 0.6); + } + + .ivu-modal-content { + box-shadow: 0 4px 12px rgba(255, 255, 255, 0.15); + } + + .ivu-tooltip-inner, + .ivu-poptip-inner { + border: 1px solid #e3e8ed; + box-shadow: none; + } + + .el-dropdown-menu { + border-color: #e3e8ed; + box-shadow: 0 2px 12px 0 rgba(255, 255, 255, 0.1); + } + + .el-tooltip__popper { + &.is-light { + border: 1px solid #e3e8ed; + box-shadow: 0 1px 6px rgba(255, 255, 255, 0.2); + } + } + + .drawer-overlay { + background: rgba(240, 240, 240, 0.76); + .overlay-body { + .overlay-close { + > a { + color: #323232; + } + } + } + } + + .file-content, + .file-preview { + .content-body { + .tox { + .tox-edit-area__iframe { + background-color: #000; + } + } + .teditor-loadedstyle { + .tox-sidebar-wrap { + box-shadow: none + } + } + } + } + + .minder-editor-container { + .quickbar { + border: 1px solid #e3e8ed !important; + box-shadow: 3px 3px 10px rgba(255, 255, 255, .2) !important; + } + } + + .project-list { + .project-head { + .project-titbox { + .project-icons { + > li { + &.project-icon { + &.active { + color: #000000; + } + } + } + } + } + } + } + + .page-manage { + .manage-box-menu { + .manage-box-dropdown { + .manage-box-title { + &.menu-visible { + box-shadow: 0 1px 6px rgba(255, 255, 255, 0.2); + } + } + } + } + } + + .page-dashboard { + .dashboard-wrapper { + .dashboard-block { + > li { + .block-title { + color: rgba(0, 0, 0, 0.6); + font-size: 12px; + } + .block-data { + .block-num { + color: #000; + } + .taskfont { + color: rgba(0, 0, 0, 0.7); + } + } + } + } + } + } + + .page-messenger { + .messenger-wrapper { + .messenger-select { + .messenger-list { + > ul { + &.dialog { + > li { + .icon-avatar { + color: #1c1917; + } + } + } + } + } + } + } + } + + .page-file { + .file-wrapper { + .file-list { + > ul { + > li { + .file-icon { + .share-icon, + .share-avatar { + .taskfont { + color: #3a3a3a; + } + } + } + } + } + } + } + } +} diff --git a/resources/assets/statics/public/js/grapheditor/index.html b/resources/assets/statics/public/js/grapheditor/index.html index c0dba1ee..08f3e85f 100644 --- a/resources/assets/statics/public/js/grapheditor/index.html +++ b/resources/assets/statics/public/js/grapheditor/index.html @@ -44,6 +44,7 @@ +