modify web page (#1122)
* modify web page
| | |
| | | "whatwg-fetch": "^3.6.2" |
| | | }, |
| | | "dependencies": { |
| | | "@vue/vue-loader-v15": { |
| | | "version": "npm:vue-loader@15.11.1", |
| | | "resolved": "http://27.17.3.62:20000/vue-loader/-/vue-loader-15.11.1.tgz", |
| | | "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", |
| | | "dev": true, |
| | | "requires": { |
| | | "@vue/component-compiler-utils": "^3.1.0", |
| | | "hash-sum": "^1.0.2", |
| | | "loader-utils": "^1.1.0", |
| | | "vue-hot-reload-api": "^2.3.0", |
| | | "vue-style-loader": "^4.1.0" |
| | | }, |
| | | "dependencies": { |
| | | "hash-sum": { |
| | | "version": "1.0.2", |
| | | "resolved": "http://27.17.3.62:20000/hash-sum/-/hash-sum-1.0.2.tgz", |
| | | "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", |
| | | "dev": true |
| | | } |
| | | } |
| | | }, |
| | | "debug": { |
| | | "version": "4.3.4", |
| | | "resolved": "http://27.17.3.62:20000/debug/-/debug-4.3.4.tgz", |
| | |
| | | "dev": true, |
| | | "requires": { |
| | | "ms": "2.1.2" |
| | | } |
| | | }, |
| | | "json5": { |
| | | "version": "1.0.2", |
| | | "resolved": "http://27.17.3.62:20000/json5/-/json5-1.0.2.tgz", |
| | | "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", |
| | | "dev": true, |
| | | "requires": { |
| | | "minimist": "^1.2.0" |
| | | } |
| | | }, |
| | | "loader-utils": { |
| | | "version": "1.4.2", |
| | | "resolved": "http://27.17.3.62:20000/loader-utils/-/loader-utils-1.4.2.tgz", |
| | | "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", |
| | | "dev": true, |
| | | "requires": { |
| | | "big.js": "^5.2.2", |
| | | "emojis-list": "^3.0.0", |
| | | "json5": "^1.0.1" |
| | | } |
| | | }, |
| | | "ms": { |
| | |
| | | "eslint-config-standard": "^16.0.3", |
| | | "eslint-import-resolver-node": "^0.3.4", |
| | | "eslint-import-resolver-webpack": "^0.13.1" |
| | | } |
| | | }, |
| | | "@vue/vue-loader-v15": { |
| | | "version": "npm:vue-loader@15.11.1", |
| | | "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.11.1.tgz", |
| | | "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", |
| | | "dev": true, |
| | | "requires": { |
| | | "@vue/component-compiler-utils": "^3.1.0", |
| | | "hash-sum": "^1.0.2", |
| | | "loader-utils": "^1.1.0", |
| | | "vue-hot-reload-api": "^2.3.0", |
| | | "vue-style-loader": "^4.1.0" |
| | | }, |
| | | "dependencies": { |
| | | "hash-sum": { |
| | | "version": "1.0.2", |
| | | "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", |
| | | "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", |
| | | "dev": true |
| | | }, |
| | | "json5": { |
| | | "version": "1.0.2", |
| | | "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", |
| | | "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", |
| | | "dev": true, |
| | | "requires": { |
| | | "minimist": "^1.2.0" |
| | | } |
| | | }, |
| | | "loader-utils": { |
| | | "version": "1.4.2", |
| | | "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", |
| | | "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", |
| | | "dev": true, |
| | | "requires": { |
| | | "big.js": "^5.2.2", |
| | | "emojis-list": "^3.0.0", |
| | | "json5": "^1.0.1" |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | "@vue/web-component-wrapper": { |
| | |
| | | }, |
| | | "vue-hot-reload-api": { |
| | | "version": "2.3.4", |
| | | "resolved": "http://27.17.3.62:20000/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", |
| | | "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", |
| | | "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", |
| | | "dev": true |
| | | }, |
| | |
| | | "core-js": "3.22.5", |
| | | "mockjs": "1.1.0", |
| | | "vue": "2.6.14", |
| | | "plugin-component-scrollbar": "1.0.12", |
| | | "vue-router": "3.5.3", |
| | | "vuex": "3.6.2", |
| | | "swiper": "5.4.5", |
| New file |
| | |
| | | .ui-scrollbar { |
| | | overflow: hidden; |
| | | position: relative; |
| | | |
| | | &.child-over-width { |
| | | .ui-scrollbar__view { |
| | | max-width: 100%; |
| | | } |
| | | } |
| | | |
| | | &:active > .ui-scrollbar__bar, |
| | | &:focus > .ui-scrollbar__bar, |
| | | &:hover > .ui-scrollbar__bar { |
| | | opacity: 1; |
| | | transition: opacity 0.34s ease-out; |
| | | } |
| | | } |
| | | |
| | | .ui-scrollbar__wrap { |
| | | overflow: scroll; |
| | | height: 100%; |
| | | } |
| | | .ui-scrollbar__view { |
| | | display: inline-block; |
| | | vertical-align: middle; |
| | | min-width: 100%; |
| | | } |
| | | |
| | | .ui-scrollbar__bar { |
| | | position: absolute; |
| | | right: 2px; |
| | | bottom: 2px; |
| | | z-index: 1; |
| | | border-radius: 4px; |
| | | opacity: 0; |
| | | -webkit-transition: opacity 120ms ease-out; |
| | | transition: opacity 120ms ease-out; |
| | | |
| | | &.is-horizontal { |
| | | height: 6px; |
| | | left: 2px; |
| | | |
| | | & > div { |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | &.is-vertical { |
| | | width: 6px; |
| | | top: 2px; |
| | | |
| | | & > div { |
| | | width: 100%; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .ui-scrollbar__thumb { |
| | | position: relative; |
| | | display: block; |
| | | width: 0; |
| | | height: 0; |
| | | cursor: pointer; |
| | | border-radius: inherit; |
| | | background-color: rgba(144, 147, 153, 0.3); |
| | | -webkit-transition: 0.3s background-color; |
| | | transition: 0.3s background-color; |
| | | |
| | | &:hover { |
| | | background-color: hsla(220, 4%, 58%, 0.5); |
| | | } |
| | | } |
| New file |
| | |
| | | <script> |
| | | import { on, off, renderThumbStyle, BAR_MAP } from './util' |
| | | |
| | | export default { |
| | | name: 'scrollbar-Bar', |
| | | props: { |
| | | vertical: Boolean, |
| | | size: String, |
| | | move: Number |
| | | }, |
| | | computed: { |
| | | bar () { |
| | | return BAR_MAP[this.vertical ? 'vertical' : 'horizontal'] |
| | | }, |
| | | |
| | | wrap () { |
| | | return this.$parent.wrap |
| | | } |
| | | }, |
| | | destroyed () { |
| | | off(document, 'mouseup', this.mouseUpDocumentHandler) |
| | | }, |
| | | methods: { |
| | | clickThumbHandler (e) { |
| | | // prevent click event of right button |
| | | if (e.ctrlKey || e.button === 2) { |
| | | return |
| | | } |
| | | this.startDrag(e) |
| | | this[this.bar.axis] = |
| | | e.currentTarget[this.bar.offset] - |
| | | (e[this.bar.client] - |
| | | e.currentTarget.getBoundingClientRect()[this.bar.direction]) |
| | | }, |
| | | |
| | | clickTrackHandler (e) { |
| | | const offset = Math.abs( |
| | | e.target.getBoundingClientRect()[this.bar.direction] - |
| | | e[this.bar.client] |
| | | ) |
| | | const thumbHalf = this.$refs.thumb[this.bar.offset] / 2 |
| | | const thumbPositionPercentage = |
| | | ((offset - thumbHalf) * 100) / this.$el[this.bar.offset] |
| | | |
| | | this.wrap[this.bar.scroll] = |
| | | (thumbPositionPercentage * this.wrap[this.bar.scrollSize]) / |
| | | 100 |
| | | }, |
| | | |
| | | startDrag (e) { |
| | | e.stopImmediatePropagation() |
| | | this.cursorDown = true |
| | | |
| | | on(document, 'mousemove', this.mouseMoveDocumentHandler) |
| | | on(document, 'mouseup', this.mouseUpDocumentHandler) |
| | | document.onselectstart = () => false |
| | | }, |
| | | |
| | | mouseMoveDocumentHandler (e) { |
| | | if (this.cursorDown === false) return |
| | | const prevPage = this[this.bar.axis] |
| | | |
| | | if (!prevPage) return |
| | | |
| | | const offset = |
| | | (this.$el.getBoundingClientRect()[this.bar.direction] - |
| | | e[this.bar.client]) * |
| | | -1 |
| | | const thumbClickPosition = |
| | | this.$refs.thumb[this.bar.offset] - prevPage |
| | | const thumbPositionPercentage = |
| | | ((offset - thumbClickPosition) * 100) / |
| | | this.$el[this.bar.offset] |
| | | |
| | | this.wrap[this.bar.scroll] = |
| | | (thumbPositionPercentage * this.wrap[this.bar.scrollSize]) / |
| | | 100 |
| | | }, |
| | | |
| | | mouseUpDocumentHandler (e) { |
| | | this.cursorDown = false |
| | | this[this.bar.axis] = 0 |
| | | off(document, 'mousemove', this.mouseMoveDocumentHandler) |
| | | document.onselectstart = null |
| | | } |
| | | }, |
| | | render (h) { |
| | | const { size, move, bar } = this |
| | | return ( |
| | | <div |
| | | class={['ui-scrollbar__bar', 'is-' + bar.key]} |
| | | onMousedown={this.clickTrackHandler} |
| | | > |
| | | <div |
| | | ref="thumb" |
| | | class="ui-scrollbar__thumb" |
| | | onMousedown={this.clickThumbHandler} |
| | | style={renderThumbStyle({ size, move, bar })} |
| | | ></div> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | </script> |
| New file |
| | |
| | | |
| | | <script> |
| | | import { addResizeListener, removeResizeListener, toObject } from './util' |
| | | import scrollbarWidth from './scrollbar-width' |
| | | import Bar from './bar.vue' |
| | | require('./assets/css/index.scss') |
| | | |
| | | export default { |
| | | name: 'ui-scrollbar', |
| | | components: { Bar }, |
| | | props: { |
| | | native: Boolean, |
| | | childOverWidth: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | wrapStyle: {}, |
| | | wrapClass: {}, |
| | | viewClass: {}, |
| | | viewStyle: {}, |
| | | noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能 |
| | | tag: { |
| | | type: String, |
| | | default: 'div' |
| | | } |
| | | }, |
| | | |
| | | data () { |
| | | return { |
| | | sizeWidth: '0', |
| | | sizeHeight: '0', |
| | | moveX: 0, |
| | | moveY: 0, |
| | | scrollbarMiddle: 0 |
| | | } |
| | | }, |
| | | |
| | | computed: { |
| | | wrap () { |
| | | return this.$refs.wrap |
| | | } |
| | | }, |
| | | |
| | | mounted () { |
| | | if (this.native) return |
| | | this.$nextTick(this.update) |
| | | !this.noresize && addResizeListener(this.$refs.resize, this.update) |
| | | !this.noresize && addResizeListener(this.$refs.wrap, this.update) |
| | | }, |
| | | |
| | | beforeDestroy () { |
| | | if (this.native) return |
| | | !this.noresize && removeResizeListener(this.$refs.resize, this.update) |
| | | !this.noresize && removeResizeListener(this.$refs.wrap, this.update) |
| | | }, |
| | | |
| | | methods: { |
| | | handleScroll () { |
| | | const wrap = this.wrap |
| | | |
| | | this.moveY = (wrap.scrollTop * 100) / wrap.clientHeight |
| | | this.moveX = (wrap.scrollLeft * 100) / wrap.clientWidth |
| | | if (wrap.scrollTop + wrap.clientHeight === wrap.scrollHeight) { |
| | | this.$emit('scrollbottom') |
| | | } |
| | | if (wrap.scrollTop === 0) { |
| | | this.$emit('scrolltop') |
| | | } |
| | | this.$emit('scrollMove', { |
| | | moveX: wrap.scrollLeft, |
| | | moveY: wrap.scrollTop, |
| | | clientX: wrap.clientWidth, |
| | | clientY: wrap.clientHeight, |
| | | scrollHeight: wrap.scrollHeight, |
| | | scrollWidth: wrap.scrollWidth |
| | | }) |
| | | }, |
| | | |
| | | update () { |
| | | let heightPercentage = null |
| | | let widthPercentage = null |
| | | const wrap = this.wrap |
| | | if (!wrap) return |
| | | |
| | | heightPercentage = (wrap.clientHeight * 100) / wrap.scrollHeight |
| | | widthPercentage = (wrap.clientWidth * 100) / wrap.scrollWidth |
| | | |
| | | this.sizeHeight = heightPercentage < 100 ? heightPercentage + '%' : '' |
| | | this.sizeWidth = widthPercentage < 100 ? widthPercentage + '%' : '' |
| | | }, |
| | | continueScroll (rowNum) { |
| | | const wrap = this.wrap |
| | | if (this.scrollbarMiddle === 0) { |
| | | this.scrollbarMiddle = wrap.scrollTop / 2 + wrap.clientHeight / 2 - wrap.clientHeight / rowNum |
| | | } |
| | | wrap.scrollTop = this.scrollbarMiddle |
| | | } |
| | | }, |
| | | |
| | | render (h) { |
| | | const gutter = scrollbarWidth() |
| | | let style = this.wrapStyle |
| | | |
| | | if (gutter) { |
| | | const gutterWith = `-${gutter}px` |
| | | let gutterStyle = '' |
| | | |
| | | let overFlowXStr = '' |
| | | let overflowYStr = '' |
| | | if (style && style.length > 0) { |
| | | style.forEach((styleItem) => { |
| | | if (styleItem['overflow-x'] || styleItem.overflowX) { |
| | | overFlowXStr = styleItem['overflow-x'] || styleItem.overflowX |
| | | } |
| | | if (styleItem['overflow-y'] || styleItem.overflowY) { |
| | | overflowYStr = styleItem['overflow-y'] || styleItem.overflowY |
| | | } |
| | | }) |
| | | } |
| | | |
| | | if (overFlowXStr === 'hidden') { |
| | | gutterStyle = `margin-right: ${gutterWith};` |
| | | } else { |
| | | gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};` |
| | | } |
| | | |
| | | if (Array.isArray(this.wrapStyle)) { |
| | | style = toObject(this.wrapStyle) |
| | | if (overFlowXStr !== 'hidden') { |
| | | style.marginBottom = gutterWith |
| | | } |
| | | if (overflowYStr !== 'hidden') { |
| | | style.marginRight = gutterWith |
| | | } |
| | | } else if (typeof this.wrapStyle === 'string') { |
| | | style += gutterStyle |
| | | } else { |
| | | style = gutterStyle |
| | | } |
| | | } |
| | | const view = h( |
| | | this.tag, |
| | | { |
| | | class: ['ui-scrollbar__view', this.viewClass], |
| | | style: this.viewStyle, |
| | | ref: 'resize' |
| | | }, |
| | | this.$slots.default |
| | | ) |
| | | const wrap = ( |
| | | <div |
| | | ref="wrap" |
| | | style={style} |
| | | onScroll={this.handleScroll} |
| | | class={[this.wrapClass, 'ui-scrollbar__wrap', gutter ? '' : 'ui-scrollbar__wrap--hidden-default']} |
| | | >{[view]}</div> |
| | | ) |
| | | let nodes |
| | | |
| | | if (!this.native) { |
| | | nodes = [ |
| | | wrap, |
| | | <Bar move={this.moveX} size={this.sizeWidth}></Bar>, |
| | | <Bar vertical move={this.moveY} size={this.sizeHeight}></Bar> |
| | | ] |
| | | } else { |
| | | nodes = [ |
| | | <div ref="wrap" class={[this.wrapClass, 'ui-scrollbar__wrap']} style={style}>{[view]}</div> |
| | | ] |
| | | } |
| | | return h('div', { class: ['ui-scrollbar', { 'child-over-width': !this.childOverWidth }] }, nodes) |
| | | } |
| | | } |
| | | </script> |
| New file |
| | |
| | | import Vue from 'vue' |
| | | |
| | | let scrollBarWidth |
| | | |
| | | export default function () { |
| | | if (Vue.prototype.$isServer) return 0 |
| | | if (scrollBarWidth !== undefined) return scrollBarWidth |
| | | |
| | | const outer = document.createElement('div') |
| | | outer.className = 'ui-scrollbar__wrap' |
| | | outer.style.visibility = 'hidden' |
| | | outer.style.width = '100px' |
| | | outer.style.position = 'absolute' |
| | | outer.style.top = '-9999px' |
| | | document.body.appendChild(outer) |
| | | |
| | | const widthNoScroll = outer.offsetWidth |
| | | outer.style.overflow = 'scroll' |
| | | |
| | | const inner = document.createElement('div') |
| | | inner.style.width = '100%' |
| | | outer.appendChild(inner) |
| | | |
| | | const widthWithScroll = inner.offsetWidth |
| | | outer.parentNode.removeChild(outer) |
| | | scrollBarWidth = widthNoScroll - widthWithScroll |
| | | |
| | | return scrollBarWidth |
| | | }; |
| New file |
| | |
| | | import ResizeObserver from 'resize-observer-polyfill' |
| | | |
| | | export const BAR_MAP = { |
| | | vertical: { |
| | | offset: 'offsetHeight', |
| | | scroll: 'scrollTop', |
| | | scrollSize: 'scrollHeight', |
| | | size: 'height', |
| | | key: 'vertical', |
| | | axis: 'Y', |
| | | client: 'clientY', |
| | | direction: 'top' |
| | | }, |
| | | horizontal: { |
| | | offset: 'offsetWidth', |
| | | scroll: 'scrollLeft', |
| | | scrollSize: 'scrollWidth', |
| | | size: 'width', |
| | | key: 'horizontal', |
| | | axis: 'X', |
| | | client: 'clientX', |
| | | direction: 'left' |
| | | } |
| | | } |
| | | |
| | | export function renderThumbStyle ({ move, size, bar }) { |
| | | const style = {} |
| | | const translate = `translate${bar.axis}(${move}%)` |
| | | |
| | | style[bar.size] = size |
| | | style.transform = translate |
| | | style.msTransform = translate |
| | | style.webkitTransform = translate |
| | | |
| | | return style |
| | | }; |
| | | |
| | | const isServer = typeof window === 'undefined' |
| | | |
| | | /* istanbul ignore next */ |
| | | const resizeHandler = function (entries) { |
| | | for (const entry of entries) { |
| | | const listeners = entry.target.__resizeListeners__ || [] |
| | | if (listeners.length) { |
| | | listeners.forEach(fn => { |
| | | fn() |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* istanbul ignore next */ |
| | | export const addResizeListener = function (element, fn) { |
| | | if (isServer) return |
| | | if (!element.__resizeListeners__) { |
| | | element.__resizeListeners__ = [] |
| | | element.__ro__ = new ResizeObserver(resizeHandler) |
| | | element.__ro__.observe(element) |
| | | } |
| | | element.__resizeListeners__.push(fn) |
| | | } |
| | | |
| | | /* istanbul ignore next */ |
| | | export const removeResizeListener = function (element, fn) { |
| | | if (!element || !element.__resizeListeners__) return |
| | | element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1) |
| | | if (!element.__resizeListeners__.length) { |
| | | element.__ro__.disconnect() |
| | | } |
| | | } |
| | | |
| | | function extend (to, _from) { |
| | | for (const key in _from) { |
| | | to[key] = _from[key] |
| | | } |
| | | return to |
| | | }; |
| | | |
| | | export function toObject (arr) { |
| | | const res = {} |
| | | for (let i = 0; i < arr.length; i++) { |
| | | if (arr[i]) { |
| | | extend(res, arr[i]) |
| | | } |
| | | } |
| | | return res |
| | | }; |
| | | |
| | | export const on = (function () { |
| | | if (!isServer && document.addEventListener) { |
| | | return function (element, event, handler) { |
| | | if (element && event && handler) { |
| | | element.addEventListener(event, handler, false) |
| | | } |
| | | } |
| | | } else { |
| | | return function (element, event, handler) { |
| | | if (element && event && handler) { |
| | | element.attachEvent('on' + event, handler) |
| | | } |
| | | } |
| | | } |
| | | })() |
| | | |
| | | /* istanbul ignore next */ |
| | | export const off = (function () { |
| | | if (!isServer && document.removeEventListener) { |
| | | return function (element, event, handler) { |
| | | if (element && event) { |
| | | element.removeEventListener(event, handler, false) |
| | | } |
| | | } |
| | | } else { |
| | | return function (element, event, handler) { |
| | | if (element && event) { |
| | | element.detachEvent('on' + event, handler) |
| | | } |
| | | } |
| | | } |
| | | })() |
| | |
| | | // 组件 |
| | | import uiJessibuca from '@/components/ui-jessibuca/index.vue' |
| | | import uiScrollbar from '@/components/ui-scrollbar/index.vue' |
| | | |
| | | const components = { |
| | | // 组件 |
| | | uiJessibuca |
| | | uiJessibuca, |
| | | uiScrollbar |
| | | } |
| | | |
| | | const install = function (Vue, opts = {}) { |
| | |
| | | // 项目本地全局方法 |
| | | import globalFunctions from './globalFunctions' |
| | | |
| | | import pluginComponentScrollbar from 'plugin-component-scrollbar' |
| | | |
| | | // 第三方组件 |
| | | // ant-design-vue 组件 按需加载 |
| | | import { ConfigProvider, Input } from 'ant-design-vue' |
| | |
| | | |
| | | Vue.use(ConfigProvider) |
| | | Vue.use(Input) |
| | | |
| | | Vue.use(pluginComponentScrollbar) |
| | | |
| | | const axiosInstance = Axios.create({ |
| | | timeout: 60000 |