| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div ref="rightPanel" :class="{show:show}" class="rightPanel-container"> |
| | | <div class="rightPanel-background" /> |
| | | <div class="rightPanel"> |
| | | <div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show"> |
| | | <i :class="show?'el-icon-close':'el-icon-setting'" /> |
| | | </div> |
| | | <div class="rightPanel-items"> |
| | | <slot /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { addClass, removeClass } from '@/utils' |
| | | |
| | | export default { |
| | | name: 'RightPanel', |
| | | props: { |
| | | clickNotClose: { |
| | | default: false, |
| | | type: Boolean |
| | | }, |
| | | buttonTop: { |
| | | default: 250, |
| | | type: Number |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | show: false |
| | | } |
| | | }, |
| | | computed: { |
| | | theme() { |
| | | return this.$store.state.settings.theme |
| | | } |
| | | }, |
| | | watch: { |
| | | show(value) { |
| | | if (value && !this.clickNotClose) { |
| | | this.addEventClick() |
| | | } |
| | | if (value) { |
| | | addClass(document.body, 'showRightPanel') |
| | | } else { |
| | | removeClass(document.body, 'showRightPanel') |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.insertToBody() |
| | | }, |
| | | beforeDestroy() { |
| | | const elx = this.$refs.rightPanel |
| | | elx.remove() |
| | | }, |
| | | methods: { |
| | | addEventClick() { |
| | | window.addEventListener('click', this.closeSidebar) |
| | | }, |
| | | closeSidebar(evt) { |
| | | const parent = evt.target.closest('.rightPanel') |
| | | if (!parent) { |
| | | this.show = false |
| | | window.removeEventListener('click', this.closeSidebar) |
| | | } |
| | | }, |
| | | insertToBody() { |
| | | const elx = this.$refs.rightPanel |
| | | const body = document.querySelector('body') |
| | | body.insertBefore(elx, body.firstChild) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .showRightPanel { |
| | | overflow: hidden; |
| | | position: relative; |
| | | width: calc(100% - 15px); |
| | | } |
| | | </style> |
| | | |
| | | <style lang="scss" scoped> |
| | | .rightPanel-background { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | opacity: 0; |
| | | transition: opacity .3s cubic-bezier(.7, .3, .1, 1); |
| | | background: rgba(0, 0, 0, .2); |
| | | z-index: -1; |
| | | } |
| | | |
| | | .rightPanel { |
| | | width: 100%; |
| | | max-width: 260px; |
| | | height: 100vh; |
| | | position: fixed; |
| | | top: 0; |
| | | right: 0; |
| | | box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05); |
| | | transition: all .25s cubic-bezier(.7, .3, .1, 1); |
| | | transform: translate(100%); |
| | | background: #fff; |
| | | z-index: 40000; |
| | | } |
| | | |
| | | .show { |
| | | transition: all .3s cubic-bezier(.7, .3, .1, 1); |
| | | |
| | | .rightPanel-background { |
| | | z-index: 20000; |
| | | opacity: 1; |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .rightPanel { |
| | | transform: translate(0); |
| | | } |
| | | } |
| | | |
| | | .handle-button { |
| | | width: 48px; |
| | | height: 48px; |
| | | position: absolute; |
| | | left: -48px; |
| | | text-align: center; |
| | | font-size: 24px; |
| | | border-radius: 6px 0 0 6px !important; |
| | | z-index: 0; |
| | | pointer-events: auto; |
| | | cursor: pointer; |
| | | color: #fff; |
| | | line-height: 48px; |
| | | i { |
| | | font-size: 24px; |
| | | line-height: 48px; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-color-picker |
| | | v-model="theme" |
| | | :predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]" |
| | | class="theme-picker" |
| | | popper-class="theme-picker-dropdown" |
| | | /> |
| | | </template> |
| | | |
| | | <script> |
| | | const version = require('element-ui/package.json').version // element-ui version from node_modules |
| | | const ORIGINAL_THEME = '#409EFF' // default color |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | chalk: '', // content of theme-chalk css |
| | | theme: '' |
| | | } |
| | | }, |
| | | computed: { |
| | | defaultTheme() { |
| | | return this.$store.state.settings.theme |
| | | } |
| | | }, |
| | | watch: { |
| | | defaultTheme: { |
| | | handler: function(val, oldVal) { |
| | | this.theme = val |
| | | }, |
| | | immediate: true |
| | | }, |
| | | async theme(val) { |
| | | const oldVal = this.chalk ? this.theme : ORIGINAL_THEME |
| | | if (typeof val !== 'string') return |
| | | const themeCluster = this.getThemeCluster(val.replace('#', '')) |
| | | const originalCluster = this.getThemeCluster(oldVal.replace('#', '')) |
| | | console.log(themeCluster, originalCluster) |
| | | |
| | | const $message = this.$message({ |
| | | message: ' Compiling the theme', |
| | | customClass: 'theme-message', |
| | | type: 'success', |
| | | duration: 0, |
| | | iconClass: 'el-icon-loading' |
| | | }) |
| | | |
| | | const getHandler = (variable, id) => { |
| | | return () => { |
| | | const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', '')) |
| | | const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster) |
| | | |
| | | let styleTag = document.getElementById(id) |
| | | if (!styleTag) { |
| | | styleTag = document.createElement('style') |
| | | styleTag.setAttribute('id', id) |
| | | document.head.appendChild(styleTag) |
| | | } |
| | | styleTag.innerText = newStyle |
| | | } |
| | | } |
| | | |
| | | if (!this.chalk) { |
| | | const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css` |
| | | await this.getCSSString(url, 'chalk') |
| | | } |
| | | |
| | | const chalkHandler = getHandler('chalk', 'chalk-style') |
| | | |
| | | chalkHandler() |
| | | |
| | | const styles = [].slice.call(document.querySelectorAll('style')) |
| | | .filter(style => { |
| | | const text = style.innerText |
| | | return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text) |
| | | }) |
| | | styles.forEach(style => { |
| | | const { innerText } = style |
| | | if (typeof innerText !== 'string') return |
| | | style.innerText = this.updateStyle(innerText, originalCluster, themeCluster) |
| | | }) |
| | | |
| | | this.$emit('change', val) |
| | | |
| | | $message.close() |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | updateStyle(style, oldCluster, newCluster) { |
| | | let newStyle = style |
| | | oldCluster.forEach((color, index) => { |
| | | newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index]) |
| | | }) |
| | | return newStyle |
| | | }, |
| | | |
| | | getCSSString(url, variable) { |
| | | return new Promise(resolve => { |
| | | const xhr = new XMLHttpRequest() |
| | | xhr.onreadystatechange = () => { |
| | | if (xhr.readyState === 4 && xhr.status === 200) { |
| | | this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '') |
| | | resolve() |
| | | } |
| | | } |
| | | xhr.open('GET', url) |
| | | xhr.send() |
| | | }) |
| | | }, |
| | | |
| | | getThemeCluster(theme) { |
| | | const tintColor = (color, tint) => { |
| | | let red = parseInt(color.slice(0, 2), 16) |
| | | let green = parseInt(color.slice(2, 4), 16) |
| | | let blue = parseInt(color.slice(4, 6), 16) |
| | | |
| | | if (tint === 0) { // when primary color is in its rgb space |
| | | return [red, green, blue].join(',') |
| | | } else { |
| | | red += Math.round(tint * (255 - red)) |
| | | green += Math.round(tint * (255 - green)) |
| | | blue += Math.round(tint * (255 - blue)) |
| | | |
| | | red = red.toString(16) |
| | | green = green.toString(16) |
| | | blue = blue.toString(16) |
| | | |
| | | return `#${red}${green}${blue}` |
| | | } |
| | | } |
| | | |
| | | const shadeColor = (color, shade) => { |
| | | let red = parseInt(color.slice(0, 2), 16) |
| | | let green = parseInt(color.slice(2, 4), 16) |
| | | let blue = parseInt(color.slice(4, 6), 16) |
| | | |
| | | red = Math.round((1 - shade) * red) |
| | | green = Math.round((1 - shade) * green) |
| | | blue = Math.round((1 - shade) * blue) |
| | | |
| | | red = red.toString(16) |
| | | green = green.toString(16) |
| | | blue = blue.toString(16) |
| | | |
| | | return `#${red}${green}${blue}` |
| | | } |
| | | |
| | | const clusters = [theme] |
| | | for (let i = 0; i <= 9; i++) { |
| | | clusters.push(tintColor(theme, Number((i / 10).toFixed(2)))) |
| | | } |
| | | clusters.push(shadeColor(theme, 0.1)) |
| | | return clusters |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .theme-message, |
| | | .theme-picker-dropdown { |
| | | z-index: 99999 !important; |
| | | } |
| | | |
| | | .theme-picker .el-color-picker__trigger { |
| | | height: 26px !important; |
| | | width: 26px !important; |
| | | padding: 2px; |
| | | } |
| | | |
| | | .theme-picker-dropdown .el-color-dropdown__link-btn { |
| | | display: none; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="drawer-container"> |
| | | <div> |
| | | <h3 class="drawer-title">Page style setting</h3> |
| | | |
| | | <div class="drawer-item"> |
| | | <span>Theme Color</span> |
| | | <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" /> |
| | | </div> |
| | | |
| | | <!-- æ¤ä¸ä¸ªåè½ææ¶ä¸ç¨--> |
| | | <!-- <div class="drawer-item">--> |
| | | <!-- <span>Open Tags-View</span>--> |
| | | <!-- <el-switch v-model="tagsView" class="drawer-switch" />--> |
| | | <!-- </div>--> |
| | | |
| | | <!-- <div class="drawer-item">--> |
| | | <!-- <span>Fixed Header</span>--> |
| | | <!-- <el-switch v-model="fixedHeader" class="drawer-switch" />--> |
| | | <!-- </div>--> |
| | | |
| | | <!-- <div class="drawer-item">--> |
| | | <!-- <span>Sidebar Logo</span>--> |
| | | <!-- <el-switch v-model="sidebarLogo" class="drawer-switch" />--> |
| | | <!-- </div>--> |
| | | |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import ThemePicker from '@/components/ThemePicker' |
| | | |
| | | export default { |
| | | components: { ThemePicker }, |
| | | data() { |
| | | return {} |
| | | }, |
| | | computed: { |
| | | fixedHeader: { |
| | | get() { |
| | | return this.$store.state.settings.fixedHeader |
| | | }, |
| | | set(val) { |
| | | this.$store.dispatch('settings/changeSetting', { |
| | | key: 'fixedHeader', |
| | | value: val |
| | | }) |
| | | } |
| | | }, |
| | | tagsView: { |
| | | get() { |
| | | return this.$store.state.settings.tagsView |
| | | }, |
| | | set(val) { |
| | | this.$store.dispatch('settings/changeSetting', { |
| | | key: 'tagsView', |
| | | value: val |
| | | }) |
| | | } |
| | | }, |
| | | sidebarLogo: { |
| | | get() { |
| | | return this.$store.state.settings.sidebarLogo |
| | | }, |
| | | set(val) { |
| | | this.$store.dispatch('settings/changeSetting', { |
| | | key: 'sidebarLogo', |
| | | value: val |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | themeChange(val) { |
| | | this.$store.dispatch('settings/changeSetting', { |
| | | key: 'theme', |
| | | value: val |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .drawer-container { |
| | | padding: 24px; |
| | | font-size: 14px; |
| | | line-height: 1.5; |
| | | word-wrap: break-word; |
| | | |
| | | .drawer-title { |
| | | margin-bottom: 12px; |
| | | color: rgba(0, 0, 0, .85); |
| | | font-size: 14px; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .drawer-item { |
| | | color: rgba(0, 0, 0, .65); |
| | | font-size: 14px; |
| | | padding: 12px 0; |
| | | } |
| | | |
| | | .drawer-switch { |
| | | float: right |
| | | } |
| | | } |
| | | </style> |
| | |
| | | export { default as Sidebar } from './Sidebar' |
| | | export { default as AppMain } from './AppMain' |
| | | export { default as TagsView } from './TagsView' |
| | | export { default as Settings } from './Settings' |
| | |
| | | <tags-view v-if="needTagsView" /> |
| | | </div> |
| | | <app-main /> |
| | | <right-panel v-if="showSettings"> |
| | | <settings /> |
| | | </right-panel> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { Navbar, Sidebar, AppMain, TagsView } from './components' |
| | | import { Navbar, Sidebar, AppMain, TagsView, Settings } from './components' |
| | | import ResizeMixin from './mixin/ResizeHandler' |
| | | import RightPanel from '@/components/RightPanel' |
| | | |
| | | export default { |
| | | name: 'Layout', |
| | |
| | | Navbar, |
| | | Sidebar, |
| | | AppMain, |
| | | TagsView |
| | | TagsView, |
| | | RightPanel, |
| | | Settings |
| | | }, |
| | | mixins: [ResizeMixin], |
| | | computed: { |
| | |
| | | }, |
| | | fixedHeader() { |
| | | return this.$store.state.settings.fixedHeader |
| | | }, |
| | | showSettings() { |
| | | return this.$store.state.settings.showSettings |
| | | }, |
| | | classObj() { |
| | | return { |
| | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import "~@/styles/mixin.scss"; |
| | | @import "~@/styles/variables.scss"; |
| | | @import "~@/styles/mixin.scss"; |
| | | @import "~@/styles/variables.scss"; |
| | | |
| | | .app-wrapper { |
| | | @include clearfix; |
| | | position: relative; |
| | | height: 100%; |
| | | width: 100%; |
| | | &.mobile.openSidebar{ |
| | | position: fixed; |
| | | top: 0; |
| | | } |
| | | } |
| | | .drawer-bg { |
| | | background: #000; |
| | | opacity: 0.3; |
| | | width: 100%; |
| | | top: 0; |
| | | height: 100%; |
| | | position: absolute; |
| | | z-index: 999; |
| | | } |
| | | .app-wrapper { |
| | | @include clearfix; |
| | | position: relative; |
| | | height: 100%; |
| | | width: 100%; |
| | | |
| | | .fixed-header { |
| | | &.mobile.openSidebar { |
| | | position: fixed; |
| | | top: 0; |
| | | right: 0; |
| | | z-index: 9; |
| | | width: calc(100% - #{$sideBarWidth}); |
| | | transition: width 0.28s; |
| | | } |
| | | } |
| | | |
| | | .hideSidebar .fixed-header { |
| | | width: calc(100% - 54px) |
| | | } |
| | | .drawer-bg { |
| | | background: #000; |
| | | opacity: 0.3; |
| | | width: 100%; |
| | | top: 0; |
| | | height: 100%; |
| | | position: absolute; |
| | | z-index: 999; |
| | | } |
| | | |
| | | .mobile .fixed-header { |
| | | width: 100%; |
| | | } |
| | | .fixed-header { |
| | | position: fixed; |
| | | top: 0; |
| | | right: 0; |
| | | z-index: 9; |
| | | width: calc(100% - #{$sideBarWidth}); |
| | | transition: width 0.28s; |
| | | } |
| | | |
| | | .hideSidebar .fixed-header { |
| | | width: calc(100% - 54px) |
| | | } |
| | | |
| | | .mobile .fixed-header { |
| | | width: 100%; |
| | | } |
| | | </style> |
| | |
| | | * @description Whether show the logo in sidebar |
| | | */ |
| | | sidebarLogo: false, |
| | | tagsView: true // tagsViewæ¾éæ§å¶ |
| | | |
| | | /** |
| | | * @type {boolean} true | false |
| | | * @description Whether show the settings right-panel |
| | | */ |
| | | showSettings: true, |
| | | |
| | | /** |
| | | * @type {boolean} true | false |
| | | * @description Whether need tagsView |
| | | */ |
| | | tagsView: true |
| | | |
| | | } |
| | |
| | | import defaultSettings from '@/settings' |
| | | import variables from '@/styles/element-variables.scss' |
| | | |
| | | const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings |
| | | |
| | | const state = { |
| | | theme: variables.theme, |
| | | showSettings: showSettings, |
| | | fixedHeader: fixedHeader, |
| | | sidebarLogo: sidebarLogo, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * I think element-ui's default theme color is too light for long-term use. |
| | | * So I modified the default color and you can modify it to your liking. |
| | | **/ |
| | | |
| | | /* theme color */ |
| | | $--color-primary: #1890ff; |
| | | $--color-success: #13ce66; |
| | | $--color-warning: #ffba00; |
| | | $--color-danger: #ff4949; |
| | | // $--color-info: #1E1E1E; |
| | | |
| | | $--button-font-weight: 400; |
| | | |
| | | // $--color-text-regular: #1f2d3d; |
| | | |
| | | $--border-color-light: #dfe4ed; |
| | | $--border-color-lighter: #e6ebf5; |
| | | |
| | | $--table-border: 1px solid #dfe6ec; |
| | | |
| | | /* icon font path, required */ |
| | | $--font-path: "~element-ui/lib/theme-chalk/fonts"; |
| | | |
| | | @import "~element-ui/packages/theme-chalk/src/index"; |
| | | |
| | | // the :export directive is the magic sauce for webpack |
| | | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass |
| | | :export { |
| | | theme: $--color-primary; |
| | | } |
| | |
| | | * @param {string} url |
| | | * @returns {Object} |
| | | */ |
| | | export function getQueryObject(url) { |
| | | url = url == null ? window.location.href : url |
| | | const search = url.substring(url.lastIndexOf('?') + 1) |
| | | const obj = {} |
| | | const reg = /([^?&=]+)=([^?&=]*)/g |
| | | search.replace(reg, (rs, $1, $2) => { |
| | | const name = decodeURIComponent($1) |
| | | let val = decodeURIComponent($2) |
| | | val = String(val) |
| | | obj[name] = val |
| | | return rs |
| | | }) |
| | | return obj |
| | | } |
| | | |
| | | /** |
| | | * @param {string} input value |
| | | * @returns {number} output value |
| | | */ |
| | | export function byteLength(str) { |
| | | // returns the byte length of an utf8 string |
| | | let s = str.length |
| | | for (var i = str.length - 1; i >= 0; i--) { |
| | | const code = str.charCodeAt(i) |
| | | if (code > 0x7f && code <= 0x7ff) s++ |
| | | else if (code > 0x7ff && code <= 0xffff) s += 2 |
| | | if (code >= 0xDC00 && code <= 0xDFFF) i-- |
| | | } |
| | | return s |
| | | } |
| | | |
| | | /** |
| | | * @param {Array} actual |
| | | * @returns {Array} |
| | | */ |
| | | export function cleanArray(actual) { |
| | | const newArray = [] |
| | | for (let i = 0; i < actual.length; i++) { |
| | | if (actual[i]) { |
| | | newArray.push(actual[i]) |
| | | } |
| | | } |
| | | return newArray |
| | | } |
| | | |
| | | /** |
| | | * @param {Object} json |
| | | * @returns {Array} |
| | | */ |
| | | export function param(json) { |
| | | if (!json) return '' |
| | | return cleanArray( |
| | | Object.keys(json).map(key => { |
| | | if (json[key] === undefined) return '' |
| | | return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) |
| | | }) |
| | | ).join('&') |
| | | } |
| | | |
| | | /** |
| | | * @param {string} url |
| | | * @returns {Object} |
| | | */ |
| | | export function param2Obj(url) { |
| | | const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') |
| | | if (!search) { |
| | |
| | | }) |
| | | return obj |
| | | } |
| | | |
| | | /** |
| | | * @param {string} val |
| | | * @returns {string} |
| | | */ |
| | | export function html2Text(val) { |
| | | const div = document.createElement('div') |
| | | div.innerHTML = val |
| | | return div.textContent || div.innerText |
| | | } |
| | | |
| | | /** |
| | | * Merges two objects, giving the last one precedence |
| | | * @param {Object} target |
| | | * @param {(Object|Array)} source |
| | | * @returns {Object} |
| | | */ |
| | | export function objectMerge(target, source) { |
| | | if (typeof target !== 'object') { |
| | | target = {} |
| | | } |
| | | if (Array.isArray(source)) { |
| | | return source.slice() |
| | | } |
| | | Object.keys(source).forEach(property => { |
| | | const sourceProperty = source[property] |
| | | if (typeof sourceProperty === 'object') { |
| | | target[property] = objectMerge(target[property], sourceProperty) |
| | | } else { |
| | | target[property] = sourceProperty |
| | | } |
| | | }) |
| | | return target |
| | | } |
| | | |
| | | /** |
| | | * @param {HTMLElement} element |
| | | * @param {string} className |
| | | */ |
| | | export function toggleClass(element, className) { |
| | | if (!element || !className) { |
| | | return |
| | | } |
| | | let classString = element.className |
| | | const nameIndex = classString.indexOf(className) |
| | | if (nameIndex === -1) { |
| | | classString += '' + className |
| | | } else { |
| | | classString = |
| | | classString.substr(0, nameIndex) + |
| | | classString.substr(nameIndex + className.length) |
| | | } |
| | | element.className = classString |
| | | } |
| | | |
| | | /** |
| | | * @param {string} type |
| | | * @returns {Date} |
| | | */ |
| | | export function getTime(type) { |
| | | if (type === 'start') { |
| | | return new Date().getTime() - 3600 * 1000 * 24 * 90 |
| | | } else { |
| | | return new Date(new Date().toDateString()) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @param {Function} func |
| | | * @param {number} wait |
| | | * @param {boolean} immediate |
| | | * @return {*} |
| | | */ |
| | | export function debounce(func, wait, immediate) { |
| | | let timeout, args, context, timestamp, result |
| | | |
| | | const later = function() { |
| | | // æ®ä¸ä¸æ¬¡è§¦åæ¶é´é´é |
| | | const last = +new Date() - timestamp |
| | | |
| | | // 䏿¬¡è¢«å
è£
彿°è¢«è°ç¨æ¶é´é´é last å°äºè®¾å®æ¶é´é´é wait |
| | | if (last < wait && last > 0) { |
| | | timeout = setTimeout(later, wait - last) |
| | | } else { |
| | | timeout = null |
| | | // å¦æè®¾å®ä¸ºimmediate===trueï¼å 为å¼å§è¾¹çå·²ç»è°ç¨è¿äºæ¤å¤æ éè°ç¨ |
| | | if (!immediate) { |
| | | result = func.apply(context, args) |
| | | if (!timeout) context = args = null |
| | | } |
| | | } |
| | | } |
| | | |
| | | return function(...args) { |
| | | context = this |
| | | timestamp = +new Date() |
| | | const callNow = immediate && !timeout |
| | | // 妿延æ¶ä¸åå¨ï¼éæ°è®¾å®å»¶æ¶ |
| | | if (!timeout) timeout = setTimeout(later, wait) |
| | | if (callNow) { |
| | | result = func.apply(context, args) |
| | | context = args = null |
| | | } |
| | | |
| | | return result |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This is just a simple version of deep copy |
| | | * Has a lot of edge cases bug |
| | | * If you want to use a perfect deep copy, use lodash's _.cloneDeep |
| | | * @param {Object} source |
| | | * @returns {Object} |
| | | */ |
| | | export function deepClone(source) { |
| | | if (!source && typeof source !== 'object') { |
| | | throw new Error('error arguments', 'deepClone') |
| | | } |
| | | const targetObj = source.constructor === Array ? [] : {} |
| | | Object.keys(source).forEach(keys => { |
| | | if (source[keys] && typeof source[keys] === 'object') { |
| | | targetObj[keys] = deepClone(source[keys]) |
| | | } else { |
| | | targetObj[keys] = source[keys] |
| | | } |
| | | }) |
| | | return targetObj |
| | | } |
| | | |
| | | /** |
| | | * @param {Array} arr |
| | | * @returns {Array} |
| | | */ |
| | | export function uniqueArr(arr) { |
| | | return Array.from(new Set(arr)) |
| | | } |
| | | |
| | | /** |
| | | * @returns {string} |
| | | */ |
| | | export function createUniqueString() { |
| | | const timestamp = +new Date() + '' |
| | | const randomNum = parseInt((1 + Math.random()) * 65536) + '' |
| | | return (+(randomNum + timestamp)).toString(32) |
| | | } |
| | | |
| | | /** |
| | | * Check if an element has a class |
| | | * @param {HTMLElement} elm |
| | | * @param {string} cls |
| | | * @returns {boolean} |
| | | */ |
| | | export function hasClass(ele, cls) { |
| | | return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) |
| | | } |
| | | |
| | | /** |
| | | * Add class to element |
| | | * @param {HTMLElement} elm |
| | | * @param {string} cls |
| | | */ |
| | | export function addClass(ele, cls) { |
| | | if (!hasClass(ele, cls)) ele.className += ' ' + cls |
| | | } |
| | | |
| | | /** |
| | | * Remove class from element |
| | | * @param {HTMLElement} elm |
| | | * @param {string} cls |
| | | */ |
| | | export function removeClass(ele, cls) { |
| | | if (hasClass(ele, cls)) { |
| | | const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') |
| | | ele.className = ele.className.replace(reg, ' ') |
| | | } |
| | | } |