feat: 🎸 layout三种布局重构完成
This commit is contained in:
parent
bd24b92acb
commit
429e42809c
package.jsonyarn.lock
src
components
Editor
Image
Logo
Preview
Scrollbar
Search
Setting
Table
directives/clipboard
libs
pages/index
axios-config
config
layout
main.tspermission.tsrouter
store/modules
views
components-demo
button
count-to
echarts
editor
image
markdown
preview
scroll
search
dashboard
directives-demo/clipboard
hooks-demo
icons
login
table-demo
basic-table
basic-usage
border-table
custom-header
custom-menu
edit-cell
edit-row
expand-row
fixed-column-header
fixed-column
fixed-header
fluid-height
multi-header
multiple-choice
screen-table
single-choice
sort-table
state-table
stripe-table
table-border
table-ellipsis
table-expanded
table-load
table-merge
table-tree
test
total-table
tree-and-load
styles
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "vue-element-admin-webpack",
|
||||
"name": "vue-element-plus-admin-webpack",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -19,7 +19,7 @@
|
|||
"clipboard": "^2.0.6",
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^4.9.0",
|
||||
"element-plus": "^1.0.1-beta.8",
|
||||
"element-plus": "1.0.1-beta.10",
|
||||
"highlight.js": "^10.4.0",
|
||||
"lodash-es": "^4.17.15",
|
||||
"mockjs": "^1.1.0",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { PropType } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { oneOf } from '@/utils'
|
||||
|
||||
import { Config } from './types'
|
||||
|
@ -18,19 +18,19 @@ export const editorProps = {
|
|||
customAlert: (s: string, t: string) => {
|
||||
switch (t) {
|
||||
case 'success':
|
||||
message.success(s)
|
||||
ElMessage.success(s)
|
||||
break
|
||||
case 'info':
|
||||
message.info(s)
|
||||
ElMessage.info(s)
|
||||
break
|
||||
case 'warning':
|
||||
message.warning(s)
|
||||
ElMessage.warning(s)
|
||||
break
|
||||
case 'error':
|
||||
message.error(s)
|
||||
ElMessage.error(s)
|
||||
break
|
||||
default:
|
||||
message.info(s)
|
||||
ElMessage.info(s)
|
||||
break
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,243 +0,0 @@
|
|||
<template>
|
||||
<div ref="imageRef" class="image">
|
||||
<slot v-if="loading" name="placeholder">
|
||||
<div class="image__placeholder" />
|
||||
</slot>
|
||||
<slot v-else-if="error" name="error">
|
||||
<div class="image__error">加载失败</div>
|
||||
</slot>
|
||||
<img
|
||||
v-else
|
||||
v-bind="$attrs"
|
||||
:src="src"
|
||||
:style="imageStyle"
|
||||
:class="{ 'image__inner--center': alignCenter }"
|
||||
class="image__inner"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, computed, watch, onMounted, onBeforeUnmount, getCurrentInstance, unref } from 'vue'
|
||||
import { on, off, getScrollContainer, isInContainer } from '@/utils/dom-utils'
|
||||
import { isString, isElement } from '@/utils/is'
|
||||
import throttle from 'lodash-es/throttle'
|
||||
|
||||
const isSupportObjectFit = () => document.documentElement.style.objectFit !== undefined
|
||||
|
||||
const ObjectFit = {
|
||||
NONE: 'none',
|
||||
CONTAIN: 'contain',
|
||||
COVER: 'cover',
|
||||
FILL: 'fill',
|
||||
SCALE_DOWN: 'scale-down'
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Image',
|
||||
// inheritAttrs: false,
|
||||
props: {
|
||||
src: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
fit: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
lazy: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
scrollContainer: {
|
||||
type: Object as PropType<any>,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
emits: ['error'],
|
||||
setup(props, { emit }) {
|
||||
const { ctx } = getCurrentInstance() as any
|
||||
|
||||
const imageRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
const error = ref<boolean>(false)
|
||||
const show = ref<boolean>(!props.lazy)
|
||||
const imageWidth = ref<number>(0)
|
||||
const imageHeight = ref<number>(0)
|
||||
const imageStyle = computed((): any => {
|
||||
const { fit } = props
|
||||
// if (!isServer && fit) {
|
||||
if (fit) {
|
||||
return isSupportObjectFit()
|
||||
? { 'object-fit': fit }
|
||||
: getImageStyle(fit)
|
||||
}
|
||||
return {}
|
||||
})
|
||||
const alignCenter = computed((): boolean => {
|
||||
const { fit } = props
|
||||
// return !isServer && !isSupportObjectFit() && fit !== ObjectFit.FILL
|
||||
return !isSupportObjectFit() && fit !== ObjectFit.FILL
|
||||
})
|
||||
|
||||
let _scrollContainer: any = null
|
||||
let _lazyLoadHandler: any = null
|
||||
|
||||
watch(
|
||||
() => show.value,
|
||||
(show: boolean) => {
|
||||
show && loadImage()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.src,
|
||||
() => {
|
||||
show.value && loadImage()
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
if (props.lazy) {
|
||||
addLazyLoadListener()
|
||||
} else {
|
||||
loadImage()
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
props.lazy && removeLazyLoadListener()
|
||||
})
|
||||
|
||||
function loadImage(): void {
|
||||
// reset status
|
||||
loading.value = true
|
||||
error.value = false
|
||||
|
||||
const img = new Image()
|
||||
img.onload = (e: any) => handleLoad(e, img)
|
||||
img.onerror = (e: any) => handleError(e)
|
||||
|
||||
// bind html attrs
|
||||
// so it can behave consistently
|
||||
Object.keys(ctx.$attrs)
|
||||
.forEach((key) => {
|
||||
const value = ctx.$attrs[key]
|
||||
img.setAttribute(key, value)
|
||||
})
|
||||
img.src = props.src
|
||||
}
|
||||
|
||||
function handleLoad(e: any, img: any): void {
|
||||
imageWidth.value = img.width
|
||||
imageHeight.value = img.height
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
function handleError(e: any): void {
|
||||
loading.value = false
|
||||
error.value = true
|
||||
emit('error', e)
|
||||
}
|
||||
|
||||
function handleLazyLoad(): void {
|
||||
const imageRefWrap = unref(imageRef) as any
|
||||
if (isInContainer(imageRefWrap, _scrollContainer)) {
|
||||
show.value = true
|
||||
removeLazyLoadListener()
|
||||
}
|
||||
}
|
||||
|
||||
function addLazyLoadListener(): void {
|
||||
// if (isServer) return
|
||||
|
||||
const { scrollContainer } = props
|
||||
let __scrollContainer = null
|
||||
|
||||
if (isElement(scrollContainer)) {
|
||||
__scrollContainer = scrollContainer
|
||||
} else if (isString(scrollContainer)) {
|
||||
__scrollContainer = document.querySelector(scrollContainer as any)
|
||||
} else {
|
||||
const imageRefWrap = unref(imageRef) as any
|
||||
__scrollContainer = getScrollContainer(imageRefWrap)
|
||||
}
|
||||
if (__scrollContainer) {
|
||||
_scrollContainer = __scrollContainer
|
||||
_lazyLoadHandler = throttle(handleLazyLoad, 200)
|
||||
on(__scrollContainer, 'scroll', _lazyLoadHandler)
|
||||
handleLazyLoad()
|
||||
}
|
||||
}
|
||||
|
||||
function removeLazyLoadListener(): void {
|
||||
// if (isServer || !_scrollContainer || !_lazyLoadHandler) return
|
||||
if (!_scrollContainer || !_lazyLoadHandler) return
|
||||
|
||||
off(_scrollContainer, 'scroll', _lazyLoadHandler)
|
||||
_scrollContainer = null
|
||||
_lazyLoadHandler = null
|
||||
}
|
||||
|
||||
/**
|
||||
* simulate object-fit behavior to compatible with IE11 and other browsers which not support object-fit
|
||||
*/
|
||||
function getImageStyle(fit: string): object {
|
||||
const imageRefWrap = unref(imageRef) as any
|
||||
const {
|
||||
clientWidth: containerWidth,
|
||||
clientHeight: containerHeight
|
||||
} = imageRefWrap
|
||||
|
||||
if (!imageWidth.value || !imageHeight.value || !containerWidth || !containerHeight) return {}
|
||||
|
||||
const vertical: boolean = imageWidth.value / imageHeight.value < 1
|
||||
|
||||
if (fit === ObjectFit.SCALE_DOWN) {
|
||||
const isSmaller: boolean = imageWidth.value < containerWidth && imageHeight.value < containerHeight
|
||||
fit = isSmaller ? ObjectFit.NONE : ObjectFit.CONTAIN
|
||||
}
|
||||
|
||||
switch (fit) {
|
||||
case ObjectFit.NONE:
|
||||
return { width: 'auto', height: 'auto' }
|
||||
case ObjectFit.CONTAIN:
|
||||
return vertical ? { width: 'auto' } : { height: 'auto' }
|
||||
case ObjectFit.COVER:
|
||||
return vertical ? { height: 'auto' } : { width: 'auto' }
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
imageRef,
|
||||
loading, error, show,
|
||||
imageStyle, alignCenter
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.image {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
.image__placeholder {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
.image__error {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #c0c4cc;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -19,7 +19,7 @@ export default defineComponent({
|
|||
},
|
||||
setup(props) {
|
||||
const show = ref<boolean>(true)
|
||||
const title = computed(() => appStore.title)
|
||||
const title = computed(() => appStore.logoTitle)
|
||||
const layout = computed(() => appStore.layout)
|
||||
watch(
|
||||
() => props.collapsed,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<div class="image-viewer__mask" />
|
||||
<!-- CLOSE -->
|
||||
<span class="image-viewer__btn image-viewer__close" @click="hide">
|
||||
<CloseCircleOutlined class="iconfont" />
|
||||
<i class="el-icon-circle-close iconfont" />
|
||||
</span>
|
||||
<!-- ARROW -->
|
||||
<template v-if="!isSingle">
|
||||
|
@ -19,14 +19,14 @@
|
|||
:class="{ 'is-disabled': !infinite && isFirst }"
|
||||
@click="prev"
|
||||
>
|
||||
<LeftOutlined class="iconfont" />
|
||||
<i class="el-icon-arrow-left iconfont" />
|
||||
</span>
|
||||
<span
|
||||
class="image-viewer__btn image-viewer__next"
|
||||
:class="{ 'is-disabled': !infinite && isLast }"
|
||||
@click="next"
|
||||
>
|
||||
<RightOutlined class="iconfont" />
|
||||
<i class="el-icon-arrow-right iconfont" />
|
||||
</span>
|
||||
</template>
|
||||
<!-- ACTIONS -->
|
||||
|
@ -63,15 +63,11 @@ import { isFirefox } from '@/utils/is'
|
|||
import { on, off } from '@/utils/dom-utils'
|
||||
import throttle from 'lodash-es/throttle'
|
||||
import SvgIcon from '_c/SvgIcon/index.vue'
|
||||
import { CloseCircleOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'
|
||||
const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel'
|
||||
export default defineComponent({
|
||||
name: 'Preview',
|
||||
components: {
|
||||
SvgIcon,
|
||||
CloseCircleOutlined,
|
||||
LeftOutlined,
|
||||
RightOutlined
|
||||
SvgIcon
|
||||
},
|
||||
props: previewProps,
|
||||
setup(props) {
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
<template>
|
||||
<div
|
||||
ref="elRef"
|
||||
:class="['scrollbar__bar', 'is-' + bar.key]"
|
||||
@mousedown="clickTrackHandler"
|
||||
>
|
||||
<div
|
||||
ref="thumbRef"
|
||||
class="scrollbar__thumb"
|
||||
:style="renderThumbStyle({ size, move, bar })"
|
||||
@mousedown="clickThumbHandler"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue'
|
||||
import { defineComponent, computed, unref, inject, Ref, reactive, ref, onBeforeUnmount } from 'vue'
|
||||
import { renderThumbStyle, BAR_MAP } from './util'
|
||||
import { on, off } from '@/utils/dom-utils'
|
||||
export default defineComponent({
|
||||
name: 'Bar',
|
||||
props: {
|
||||
vertical: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
move: {
|
||||
type: Number as PropType<number>,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const thumbRef = ref<HTMLElement | null>(null)
|
||||
const elRef = ref<HTMLElement | null>(null)
|
||||
const commonState = reactive<any>({})
|
||||
const getBarRef = computed(() => {
|
||||
return BAR_MAP[props.vertical ? 'vertical' : 'horizontal']
|
||||
})
|
||||
const bar = unref(getBarRef)
|
||||
const parentElRef = inject('scroll-bar-wrap') as Ref<HTMLElement>
|
||||
|
||||
function clickThumbHandler(e: any) {
|
||||
const { ctrlKey, button, currentTarget } = e
|
||||
// prevent click event of right button
|
||||
if (ctrlKey || button === 2 || !currentTarget) {
|
||||
return
|
||||
}
|
||||
startDrag(e)
|
||||
const bar = unref(getBarRef)
|
||||
commonState[bar.axis] =
|
||||
currentTarget[bar.offset] -
|
||||
(e[bar.client as keyof typeof e] - currentTarget.getBoundingClientRect()[bar.direction])
|
||||
}
|
||||
|
||||
function clickTrackHandler(e: any) {
|
||||
const bar = unref(getBarRef)
|
||||
const offset = Math.abs(e.target.getBoundingClientRect()[bar.direction] - e[bar.client])
|
||||
const thumbEl = unref(thumbRef) as any
|
||||
const parentEl = unref(parentElRef) as any
|
||||
const el = unref(elRef) as any
|
||||
if (!thumbEl || !el || !parentEl) return
|
||||
const thumbHalf = thumbEl[bar.offset] / 2
|
||||
const thumbPositionPercentage = ((offset - thumbHalf) * 100) / el[bar.offset]
|
||||
parentEl[bar.scroll] = (thumbPositionPercentage * parentEl[bar.scrollSize]) / 100
|
||||
}
|
||||
|
||||
function startDrag(e: Event) {
|
||||
e.stopImmediatePropagation()
|
||||
commonState.cursorDown = true
|
||||
|
||||
on(document, 'mousemove', mouseMoveDocumentHandler)
|
||||
on(document, 'mouseup', mouseUpDocumentHandler)
|
||||
document.onselectstart = () => false
|
||||
}
|
||||
|
||||
function mouseMoveDocumentHandler(e: any) {
|
||||
if (commonState.cursorDown === false) return
|
||||
const bar = unref(getBarRef)
|
||||
const prevPage = commonState[bar.axis]
|
||||
const el = unref(elRef) as any
|
||||
const parentEl = unref(parentElRef) as any
|
||||
const thumbEl = unref(thumbRef) as any
|
||||
if (!prevPage || !el || !thumbEl || !parentEl) return
|
||||
const rect = el.getBoundingClientRect() as any
|
||||
const offset = (rect[bar.direction] - e[bar.client]) * -1
|
||||
const thumbClickPosition = thumbEl[bar.offset] - prevPage
|
||||
const thumbPositionPercentage = ((offset - thumbClickPosition) * 100) / el[bar.offset]
|
||||
|
||||
parentEl[bar.scroll] = (thumbPositionPercentage * parentEl[bar.scrollSize]) / 100
|
||||
}
|
||||
|
||||
function mouseUpDocumentHandler() {
|
||||
const bar = unref(getBarRef)
|
||||
commonState.cursorDown = false
|
||||
commonState[bar.axis] = 0
|
||||
off(document, 'mousemove', mouseMoveDocumentHandler)
|
||||
document.onselectstart = null
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
off(document, 'mouseup', mouseUpDocumentHandler)
|
||||
})
|
||||
|
||||
return {
|
||||
thumbRef,
|
||||
elRef,
|
||||
bar,
|
||||
clickThumbHandler,
|
||||
clickTrackHandler,
|
||||
renderThumbStyle
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.scrollbar__bar.is-vertical>div {
|
||||
width: 100%;
|
||||
}
|
||||
.scrollbar__bar.is-horizontal>div {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,254 +0,0 @@
|
|||
<template>
|
||||
<div class="scrollbar">
|
||||
<template v-if="!native">
|
||||
<div
|
||||
ref="wrapElRef"
|
||||
:style="style"
|
||||
:class="[wrapClass, 'scrollbar__wrap', gutter ? '' : 'scrollbar__wrap--hidden-default']"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<div
|
||||
ref="resizeRef"
|
||||
:class="['scrollbar__view', viewClass]"
|
||||
:style="viewStyle"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<bar v-if="showX" :move="state.moveX" :size="state.sizeWidth" />
|
||||
<bar v-if="showY" vertical :move="state.moveY" :size="state.sizeHeight" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
ref="wrap"
|
||||
:class="[wrapClass, 'scrollbar__wrap']"
|
||||
:style="style"
|
||||
>
|
||||
<div
|
||||
ref="resizeRef"
|
||||
:class="['scrollbar__view', viewClass]"
|
||||
:style="viewStyle"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
PropType,
|
||||
unref,
|
||||
reactive,
|
||||
ref,
|
||||
toRef,
|
||||
provide,
|
||||
onMounted,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
getCurrentInstance
|
||||
} from 'vue'
|
||||
import { addResizeListener, removeResizeListener } from '@/utils/event/resize-event'
|
||||
import scrollbarWidth from '@/utils/scrollbar-width'
|
||||
import { isString } from '@/utils/is'
|
||||
import { toObject } from './util'
|
||||
import Bar from './Bar.vue'
|
||||
export default defineComponent({
|
||||
name: 'Scrollbar',
|
||||
components: {
|
||||
Bar
|
||||
},
|
||||
props: {
|
||||
native: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
wrapStyle: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => null
|
||||
},
|
||||
wrapClass: {
|
||||
type: String as PropType<string>, required: false,
|
||||
default: ''
|
||||
},
|
||||
viewClass: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
viewStyle: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => {}
|
||||
},
|
||||
noresize: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
showX: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true
|
||||
},
|
||||
showY: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true
|
||||
}
|
||||
// tag: {
|
||||
// type: String as PropType<string>,
|
||||
// default: 'div'
|
||||
// }
|
||||
},
|
||||
setup(props) {
|
||||
const resizeRef = ref<HTMLElement | null>(null)
|
||||
const wrapElRef = ref<HTMLElement | null>(null)
|
||||
provide('scroll-bar-wrap', wrapElRef)
|
||||
const state = reactive({
|
||||
sizeWidth: '0',
|
||||
sizeHeight: '0',
|
||||
moveX: 0,
|
||||
moveY: 0
|
||||
})
|
||||
let style: any = toRef(props, 'wrapStyle')
|
||||
const gutter = scrollbarWidth()
|
||||
if (gutter) {
|
||||
const gutterWith = `-${gutter}px`
|
||||
const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`
|
||||
|
||||
if (Array.isArray(props.wrapStyle)) {
|
||||
style = toObject(props.wrapStyle)
|
||||
style.value.marginRight = style.value.marginBottom = gutterWith
|
||||
} else if (isString(props.wrapStyle)) {
|
||||
style.value += gutterStyle
|
||||
} else {
|
||||
style = gutterStyle
|
||||
}
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
const warpEl = unref(wrapElRef)
|
||||
if (!warpEl) return
|
||||
const { scrollTop, scrollLeft, clientHeight, clientWidth } = warpEl
|
||||
|
||||
state.moveY = (scrollTop * 100) / clientHeight
|
||||
state.moveX = (scrollLeft * 100) / clientWidth
|
||||
}
|
||||
function update() {
|
||||
const warpEl = unref(wrapElRef)
|
||||
if (!warpEl) return
|
||||
const { scrollHeight, scrollWidth, clientHeight, clientWidth } = warpEl
|
||||
const heightPercentage = (clientHeight * 100) / scrollHeight
|
||||
const widthPercentage = (clientWidth * 100) / scrollWidth
|
||||
|
||||
state.sizeHeight = heightPercentage < 100 ? heightPercentage + '%' : ''
|
||||
state.sizeWidth = widthPercentage < 100 ? widthPercentage + '%' : ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const instance = getCurrentInstance() as any
|
||||
if (instance) {
|
||||
instance.wrap = unref(wrapElRef)
|
||||
}
|
||||
|
||||
const { native, noresize } = props
|
||||
const resizeEl = unref(resizeRef)
|
||||
const warpEl = unref(wrapElRef)
|
||||
if (native || !resizeEl || !warpEl) return
|
||||
nextTick(update)
|
||||
if (!noresize) {
|
||||
addResizeListener(resizeEl, update)
|
||||
addResizeListener(warpEl, update)
|
||||
}
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
const { native, noresize } = props
|
||||
const resizeEl = unref(resizeRef)
|
||||
const warpEl = unref(wrapElRef)
|
||||
if (native || !resizeEl || !warpEl) return
|
||||
if (!noresize) {
|
||||
removeResizeListener(resizeEl, update)
|
||||
removeResizeListener(warpEl, update)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
resizeRef, wrapElRef,
|
||||
state, gutter, style,
|
||||
handleScroll
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.scrollbar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
&__wrap {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
&--hidden-default {
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@{deep}(&__thumb) {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
cursor: pointer;
|
||||
background-color: rgba(144, 147, 153, 0.3);
|
||||
border-radius: inherit;
|
||||
transition: 0.3s background-color;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(144, 147, 153, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&__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-vertical {
|
||||
top: 2px;
|
||||
width: 6px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-horizontal {
|
||||
left: 2px;
|
||||
height: 6px;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scrollbar:active > .scrollbar__bar,
|
||||
.scrollbar:focus > .scrollbar__bar,
|
||||
.scrollbar:hover > .scrollbar__bar {
|
||||
opacity: 1;
|
||||
transition: opacity 280ms ease-out;
|
||||
}
|
||||
</style>
|
|
@ -1,14 +0,0 @@
|
|||
export interface BarMapItem {
|
||||
offset: string
|
||||
scroll: string
|
||||
scrollSize: string
|
||||
size: string
|
||||
key: string
|
||||
axis: string
|
||||
client: string
|
||||
direction: string
|
||||
}
|
||||
export interface BarMap {
|
||||
vertical: BarMapItem
|
||||
horizontal: BarMapItem
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
import type { BarMap } from './types'
|
||||
export const BAR_MAP: BarMap = {
|
||||
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 }: any) {
|
||||
const style = {} as any
|
||||
const translate = `translate${bar.axis}(${move}%)`
|
||||
|
||||
style[bar.size] = size
|
||||
style.transform = translate
|
||||
style.msTransform = translate
|
||||
style.webkitTransform = translate
|
||||
|
||||
return style
|
||||
}
|
||||
|
||||
function extend<T, K>(to: T, _from: K): T & K {
|
||||
return Object.assign(to, _from)
|
||||
}
|
||||
|
||||
export function toObject<T>(arr: Array<T>): Record<string, T> {
|
||||
const res = {}
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]) {
|
||||
extend(res, arr[i])
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
|
@ -1,96 +1,86 @@
|
|||
<template>
|
||||
<div :class="{ search__col: layout === 'right' }">
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="layout === 'right' ? 22 : 24">
|
||||
<a-form
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="layout === 'right' ? 22 : 24">
|
||||
<el-form
|
||||
ref="ruleForm"
|
||||
layout="inline"
|
||||
inline
|
||||
:model="formInline"
|
||||
:rules="rules"
|
||||
:label-col="labelCol"
|
||||
:wrapper-col="wrapperCol"
|
||||
:label-align="labelAlign"
|
||||
:hide-required-mark="hideRequiredMark"
|
||||
:label-width="labelWidth"
|
||||
:label-position="labelPosition"
|
||||
:hide-required-asterisk="hideRequiredAsterisk"
|
||||
@submit.prevent
|
||||
>
|
||||
<a-form-item
|
||||
<el-form-item
|
||||
v-for="(item, $index) in data"
|
||||
:key="$index"
|
||||
:label="item.label"
|
||||
:name="item.field"
|
||||
:prop="item.field"
|
||||
:rules="item.rules"
|
||||
>
|
||||
<template v-if="item.type === 'switch'">
|
||||
<a-switch
|
||||
v-model:checked="formInline[item.field]"
|
||||
:size="item.size"
|
||||
:checked-children="item.checkedChildren"
|
||||
:un-checked-children="item.unCheckedChildren"
|
||||
<template v-if="item.itemType === 'switch'">
|
||||
<el-switch
|
||||
v-model="formInline[item.field]"
|
||||
v-bind="{...bindValue(item)}"
|
||||
@change="((val) => {changeVal(val, item)})"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="item.type === 'input'">
|
||||
<a-input
|
||||
v-model:value="formInline[item.field]"
|
||||
:size="item.size"
|
||||
:maxlength="item.maxlength"
|
||||
:placeholder="item.placeholder"
|
||||
:addon-before="item.addonBefore"
|
||||
:addon-after="item.addonAfter"
|
||||
:allow-clear="item.allowClear"
|
||||
<template v-if="item.itemType === 'input'">
|
||||
<el-input
|
||||
v-model="formInline[item.field]"
|
||||
v-bind="{...bindValue(item)}"
|
||||
@change="((val) => {changeVal(val, item)})"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="item.type === 'select'">
|
||||
<a-select
|
||||
v-model:value="formInline[item.field]"
|
||||
:size="item.size"
|
||||
:placeholder="item.placeholder"
|
||||
:allow-clear="item.allowClear"
|
||||
style="min-width: 201px;"
|
||||
<template v-if="item.itemType === 'select'">
|
||||
<el-select
|
||||
v-model="formInline[item.field]"
|
||||
v-bind="{...bindValue(item)}"
|
||||
@change="((val) => {changeVal(val, item)})"
|
||||
>
|
||||
<a-select-option
|
||||
<el-option
|
||||
v-for="v in item.options"
|
||||
:key="item.optionValue ? v[item.optionValue] : v.value"
|
||||
:value="item.optionValue ? v[item.optionValue] : v.value"
|
||||
>
|
||||
{{ item.optionLabel ? v[item.optionLabel] : v.title }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
:label="item.optionLabel ? v[item.optionLabel] : v.title"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<template v-if="item.type === 'radio'">
|
||||
<a-radio-group
|
||||
v-model:value="formInline[item.field]"
|
||||
:size="item.size"
|
||||
<template v-if="item.itemType === 'radio'">
|
||||
<el-radio-group
|
||||
v-model="formInline[item.field]"
|
||||
@change="((val) => {changeVal(val, item)})"
|
||||
>
|
||||
<template v-if="item.radioType === 'radio'">
|
||||
<a-radio
|
||||
<el-radio
|
||||
v-for="v in item.options"
|
||||
:key="item.optionValue ? v[item.optionValue] : v.value"
|
||||
:value="item.optionValue ? v[item.optionValue] : v.value"
|
||||
v-bind="{...bindValue(item)}"
|
||||
:label="item.optionValue ? v[item.optionValue] : v.value"
|
||||
>
|
||||
{{ item.optionLabel ? v[item.optionLabel] : v.label }}
|
||||
</a-radio>
|
||||
</el-radio>
|
||||
</template>
|
||||
<template v-else-if="item.radioType === 'button'">
|
||||
<a-radio-button
|
||||
<el-radio-button
|
||||
v-for="v in item.options"
|
||||
:key="item.optionValue ? v[item.optionValue] : v.value"
|
||||
:value="item.optionValue ? v[item.optionValue] : v.value"
|
||||
v-bind="{...bindValue(item)}"
|
||||
:label="item.optionValue ? v[item.optionValue] : v.value"
|
||||
>
|
||||
{{ item.optionLabel ? v[item.optionLabel] : v.label }}
|
||||
</a-radio-button>
|
||||
</el-radio-button>
|
||||
</template>
|
||||
</a-radio-group>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
|
||||
<template v-if="item.type === 'treeSelect'">
|
||||
<a-tree-select
|
||||
<!-- element近期会新增treeSelect组件,所以不打算在自己维护一套。等待ing -->
|
||||
<!-- <template v-if="item.itemType === 'treeSelect'">
|
||||
<el-tree-select
|
||||
v-model:value="formInline[item.field]"
|
||||
:size="item.size"
|
||||
:dropdown-style="item.dropdownStyle"
|
||||
|
@ -106,161 +96,102 @@
|
|||
<template #title="{ title }">
|
||||
<span>{{ title }}</span>
|
||||
</template>
|
||||
</a-tree-select>
|
||||
</template>
|
||||
</el-tree-select>
|
||||
</template> -->
|
||||
|
||||
<template v-if="item.type === 'datePicker'">
|
||||
<a-date-picker
|
||||
v-model:value="formInline[item.field]"
|
||||
:format="item.format"
|
||||
:mode="item.mode"
|
||||
:value-format="item.valueFormat"
|
||||
:placeholder="item.placeholder"
|
||||
:size="item.size"
|
||||
:allow-clear="item.allowClear"
|
||||
:disabled-date="item.disabledDate"
|
||||
:disabled-time="item.disabledDateTime"
|
||||
:show-time="item.showTime"
|
||||
<template v-if="item.itemType === 'timePicker'">
|
||||
<el-time-picker
|
||||
v-model="formInline[item.field]"
|
||||
v-bind="{...bindValue(item)}"
|
||||
@change="((val) => {changeVal(val, item)})"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="item.type === 'monthPicker'">
|
||||
<a-month-picker
|
||||
v-model:value="formInline[item.field]"
|
||||
:format="item.format"
|
||||
:mode="item.mode"
|
||||
:value-format="item.valueFormat"
|
||||
:placeholder="item.placeholder"
|
||||
:size="item.size"
|
||||
:allow-clear="item.allowClear"
|
||||
:disabled-date="item.disabledDate"
|
||||
:disabled-time="item.disabledDateTime"
|
||||
:show-time="item.showTime"
|
||||
<template v-if="item.itemType === 'timeSelect'">
|
||||
<el-time-select
|
||||
v-model="formInline[item.field]"
|
||||
v-bind="{...bindValue(item)}"
|
||||
@change="((val) => {changeVal(val, item)})"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="item.type === 'rangePicker'">
|
||||
<a-range-picker
|
||||
v-model:value="formInline[item.field]"
|
||||
:format="item.format"
|
||||
:mode="item.mode"
|
||||
:value-format="item.valueFormat"
|
||||
:placeholder="item.placeholder"
|
||||
:size="item.size"
|
||||
:allow-clear="item.allowClear"
|
||||
:disabled-date="item.disabledDate"
|
||||
:disabled-time="item.disabledDateTime"
|
||||
:show-time="item.showTime"
|
||||
:ranges="item.ranges"
|
||||
<template v-if="item.itemType === 'datePicker' || item.itemType === 'dateTimePicker'">
|
||||
<el-date-picker
|
||||
v-model="formInline[item.field]"
|
||||
v-bind="{...bindValue(item)}"
|
||||
@change="((val) => {changeVal(val, item)})"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="item.type === 'weekPicker'">
|
||||
<a-week-picker
|
||||
v-model:value="formInline[item.field]"
|
||||
:format="item.format"
|
||||
:mode="item.mode"
|
||||
:value-format="item.valueFormat"
|
||||
:placeholder="item.placeholder"
|
||||
:size="item.size"
|
||||
:allow-clear="item.allowClear"
|
||||
:disabled-date="item.disabledDate"
|
||||
:disabled-time="item.disabledDateTime"
|
||||
:show-time="item.showTime"
|
||||
@change="((val) => {changeVal(val, item)})"
|
||||
/>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="data.length > 0 && layout === 'classic'">
|
||||
<a-button
|
||||
</el-form-item>
|
||||
<el-form-item v-if="data.length > 0 && layout === 'classic'">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="submitForm"
|
||||
>
|
||||
<template #icon>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
查询
|
||||
</a-button>
|
||||
<a-button
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="showReset"
|
||||
icon="el-icon-refresh-right"
|
||||
@click="resetForm"
|
||||
>
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-col>
|
||||
<a-col :span="layout === 'right' ? 2 : 24">
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="layout === 'right' ? 2 : 24">
|
||||
<div
|
||||
v-if="data.length > 0 && (layout === 'bottom' || layout === 'right')"
|
||||
class="search__bottom"
|
||||
:class="{ 'search__bottom--col': layout === 'right' }"
|
||||
>
|
||||
<div class="search__bottom--button">
|
||||
<a-button
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="submitForm"
|
||||
>
|
||||
<template #icon>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
查询
|
||||
</a-button>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="search__bottom--button">
|
||||
<a-button
|
||||
<el-button
|
||||
v-if="showReset"
|
||||
:style="{
|
||||
'margin-left': layout !== 'right' ? '15px' : '0',
|
||||
'margin-top': layout === 'right' ? '27px' : '0'
|
||||
}"
|
||||
icon="el-icon-refresh-right"
|
||||
@click="resetForm"
|
||||
>
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, watch, ref, unref } from 'vue'
|
||||
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons-vue'
|
||||
import { deepClone } from '@/utils'
|
||||
export default defineComponent({
|
||||
name: 'Search',
|
||||
components: {
|
||||
SearchOutlined,
|
||||
ReloadOutlined
|
||||
},
|
||||
props: {
|
||||
// label 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12} 或 sm: {span: 3, offset: 12}
|
||||
labelCol: {
|
||||
type: Object as PropType<{ span: number }>,
|
||||
default: () => {}
|
||||
// 表单域标签的宽度,例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 auto。
|
||||
labelWidth: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
// 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol
|
||||
wrapperCol: {
|
||||
type: Object as PropType<{ span: number }>,
|
||||
default: () => {}
|
||||
},
|
||||
// label 标签的文本对齐方式
|
||||
labelAlign: {
|
||||
type: String as PropType<'left' | 'right'>,
|
||||
labelPosition: {
|
||||
type: String as PropType<'right' | 'left' | 'top'>,
|
||||
default: 'right'
|
||||
},
|
||||
// 隐藏所有表单项的必选标记
|
||||
hideRequiredMark: {
|
||||
hideRequiredAsterisk: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true
|
||||
},
|
||||
|
@ -305,6 +236,17 @@ export default defineComponent({
|
|||
}
|
||||
)
|
||||
|
||||
function bindValue(item: any) {
|
||||
const delArr: string[] = ['label', 'itemType', 'value', 'field']
|
||||
const obj = deepClone(item)
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
function initForm(data: any): void {
|
||||
for (const v of data) {
|
||||
formInline.value[v.field] = formInline.value[v.field] || v.value
|
||||
|
@ -315,10 +257,15 @@ export default defineComponent({
|
|||
const form = unref(ruleForm) as any
|
||||
if (!form) return
|
||||
try {
|
||||
const data = await form.validate()
|
||||
if (data) {
|
||||
emit('search-submit', data)
|
||||
}
|
||||
form.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
console.log(valid)
|
||||
emit('search-submit', unref(formInline))
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
@ -328,19 +275,20 @@ export default defineComponent({
|
|||
const form = unref(ruleForm) as any
|
||||
if (!form) return
|
||||
await form.resetFields()
|
||||
emit('reset-submit', formInline.value)
|
||||
emit('reset-submit', unref(formInline))
|
||||
}
|
||||
|
||||
function changeVal(val: any, item: any): void {
|
||||
if (item.onChange) {
|
||||
emit('change', {
|
||||
field: item.field,
|
||||
value: formInline.value[item.field]
|
||||
value: unref(formInline.value[item.field])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
bindValue,
|
||||
ruleForm,
|
||||
formInline,
|
||||
submitForm,
|
||||
|
|
|
@ -1,72 +1,101 @@
|
|||
<template>
|
||||
<div class="setting__content">
|
||||
<div class="setting__title">导航栏布局</div>
|
||||
<div class="icon__wrap">
|
||||
<span :class="{'icon--active': layout==='Classic'}" @click="setLayout('Classic')">
|
||||
<el-tooltip effect="dark" content="经典布局" placement="bottom">
|
||||
<svg-icon icon-class="layout-classic" class="setting-svg-icon" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span :class="{'icon--active': layout==='LeftTop'}" @click="setLayout('LeftTop')">
|
||||
<el-tooltip effect="dark" content="左侧顶部布局" placement="bottom">
|
||||
<svg-icon icon-class="layout-topLeft" class="setting-svg-icon" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span :class="{'icon--active': layout==='Top'}" @click="setLayout('Top')">
|
||||
<el-tooltip effect="dark" content="顶部布局" placement="bottom">
|
||||
<svg-icon icon-class="layout-top" class="setting-svg-icon" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="setting__title">侧边菜单主题</div>
|
||||
|
||||
<div class="setting__title">顶部菜单主题</div>
|
||||
|
||||
<div class="setting__title">界面功能</div>
|
||||
<!-- <div class="setting__item">
|
||||
<span>固定顶部操作栏</span>
|
||||
<el-switch v-model="fixedNavbar" @change="setFixedNavbar" />
|
||||
</div>
|
||||
<div class="setting__item">
|
||||
<span>固定标签页</span>
|
||||
<el-switch v-model="fixedTags" @change="setFixedTags" />
|
||||
</div> -->
|
||||
<div class="setting__item">
|
||||
<span>固定Header</span>
|
||||
<el-switch v-model="fixedHeader" @change="setFixedHeader" />
|
||||
</div>
|
||||
|
||||
<div class="setting__title">界面显示</div>
|
||||
<div v-if="layout !== 'Top'" class="setting__item">
|
||||
<span>顶部操作栏</span>
|
||||
<el-switch v-model="navbar" @change="setNavbar" />
|
||||
</div>
|
||||
<div v-if="layout !== 'Top'" class="setting__item">
|
||||
<span>侧边栏缩收</span>
|
||||
<el-switch v-model="hamburger" @change="setHamburger" />
|
||||
</div>
|
||||
<div v-if="layout !== 'Top'" class="setting__item">
|
||||
<span>面包屑</span>
|
||||
<el-switch v-model="breadcrumb" @change="setBreadcrumb" />
|
||||
</div>
|
||||
<div class="setting__item">
|
||||
<span>全屏按钮</span>
|
||||
<el-switch v-model="screenfull" @change="setScreenfull" />
|
||||
</div>
|
||||
<div class="setting__item">
|
||||
<span>用户头像</span>
|
||||
<el-switch v-model="userInfo" @change="setUserInfo" />
|
||||
</div>
|
||||
<div class="setting__item">
|
||||
<span>标签页</span>
|
||||
<el-switch v-model="tagsView" @change="setTagsView" />
|
||||
</div>
|
||||
<div class="setting__item">
|
||||
<span>LOGO</span>
|
||||
<el-switch v-model="logo" @change="setLogo" />
|
||||
</div>
|
||||
<div class="setting__wrap" @click="toggleClick">
|
||||
<i class="el-icon-setting" />
|
||||
</div>
|
||||
<el-drawer
|
||||
v-model="drawer"
|
||||
direction="rtl"
|
||||
size="20%"
|
||||
>
|
||||
<template #title>
|
||||
<div class="setting__title">项目配置</div>
|
||||
</template>
|
||||
<div class="setting__content">
|
||||
<div class="setting__title">导航栏布局</div>
|
||||
<div class="icon__wrap">
|
||||
<span :class="{'icon--active': layout==='Classic'}" @click="setLayout('Classic')">
|
||||
<el-tooltip effect="dark" content="经典布局" placement="bottom">
|
||||
<svg-icon icon-class="layout-classic" class="setting-svg-icon" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span :class="{'icon--active': layout==='LeftTop'}" @click="setLayout('LeftTop')">
|
||||
<el-tooltip effect="dark" content="左侧顶部布局" placement="bottom">
|
||||
<svg-icon icon-class="layout-topLeft" class="setting-svg-icon" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span :class="{'icon--active': layout==='Top'}" @click="setLayout('Top')">
|
||||
<el-tooltip effect="dark" content="顶部布局" placement="bottom">
|
||||
<svg-icon icon-class="layout-top" class="setting-svg-icon" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- <div class="setting__title">侧边菜单主题</div>
|
||||
|
||||
<div class="setting__title">顶部菜单主题</div> -->
|
||||
|
||||
<!-- <div class="setting__title">界面功能</div> -->
|
||||
<!-- <div class="setting__item">
|
||||
<span>固定顶部操作栏</span>
|
||||
<el-switch v-model="fixedNavbar" @change="setFixedNavbar" />
|
||||
</div>
|
||||
<div class="setting__item">
|
||||
<span>固定标签页</span>
|
||||
<el-switch v-model="fixedTags" @change="setFixedTags" />
|
||||
</div> -->
|
||||
|
||||
<div class="setting__title">界面显示</div>
|
||||
<div class="setting__item">
|
||||
<span>固定Header</span>
|
||||
<el-switch v-model="fixedHeader" @change="setFixedHeader" />
|
||||
</div>
|
||||
|
||||
<div v-if="layout !== 'Top'" class="setting__item">
|
||||
<span>顶部操作栏</span>
|
||||
<el-switch v-model="navbar" @change="setNavbar" />
|
||||
</div>
|
||||
|
||||
<div v-if="layout !== 'Top'" class="setting__item">
|
||||
<span>侧边栏缩收</span>
|
||||
<el-switch v-model="hamburger" @change="setHamburger" />
|
||||
</div>
|
||||
|
||||
<div v-if="layout !== 'Top'" class="setting__item">
|
||||
<span>面包屑</span>
|
||||
<el-switch v-model="breadcrumb" @change="setBreadcrumb" />
|
||||
</div>
|
||||
|
||||
<div class="setting__item">
|
||||
<span>全屏按钮</span>
|
||||
<el-switch v-model="screenfull" @change="setScreenfull" />
|
||||
</div>
|
||||
|
||||
<div class="setting__item">
|
||||
<span>用户头像</span>
|
||||
<el-switch v-model="userInfo" @change="setUserInfo" />
|
||||
</div>
|
||||
|
||||
<div class="setting__item">
|
||||
<span>标签页</span>
|
||||
<el-switch v-model="tagsView" @change="setTagsView" />
|
||||
</div>
|
||||
|
||||
<div class="setting__item">
|
||||
<span>LOGO</span>
|
||||
<el-switch v-model="logo" @change="setLogo" />
|
||||
</div>
|
||||
|
||||
<div class="setting__item">
|
||||
<span>页面标题</span>
|
||||
<el-input v-model="title" size="mini" @change="setTitle" />
|
||||
</div>
|
||||
|
||||
<div class="setting__item">
|
||||
<span>LOGO标题</span>
|
||||
<el-input v-model="logoTitle" size="mini" @change="setLogoTitle" />
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -75,6 +104,11 @@ import { appStore } from '_p/index/store/modules/app'
|
|||
export default defineComponent({
|
||||
name: 'Setting',
|
||||
setup() {
|
||||
const drawer = ref<boolean>(false)
|
||||
function toggleClick(): void {
|
||||
drawer.value = !drawer.value
|
||||
}
|
||||
|
||||
const layout = computed(() => appStore.layout)
|
||||
function setLayout(mode: 'Classic' | 'LeftTop' | 'Top' | 'Test') {
|
||||
if (mode === layout.value) return
|
||||
|
@ -132,7 +166,18 @@ export default defineComponent({
|
|||
appStore.SetShowLogo(logo)
|
||||
}
|
||||
|
||||
const title = ref<string>(appStore.title)
|
||||
function setTitle(title: string) {
|
||||
appStore.SetTitle(title)
|
||||
}
|
||||
|
||||
const logoTitle = ref<string>(appStore.logoTitle)
|
||||
function setLogoTitle(logoTitle: string) {
|
||||
appStore.SetLogoTitle(logoTitle)
|
||||
}
|
||||
|
||||
return {
|
||||
drawer, toggleClick,
|
||||
layout, setLayout,
|
||||
// fixedNavbar, setFixedNavbar,
|
||||
// fixedTags, setFixedTags,
|
||||
|
@ -143,13 +188,35 @@ export default defineComponent({
|
|||
screenfull, setScreenfull,
|
||||
userInfo, setUserInfo,
|
||||
tagsView, setTagsView,
|
||||
logo, setLogo
|
||||
logo, setLogo,
|
||||
title, setTitle,
|
||||
logoTitle, setLogoTitle
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// 项目配置
|
||||
.setting__wrap {
|
||||
position: fixed;
|
||||
top: 45%;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
background: #018ffb;
|
||||
border-radius: 6px 0 0 6px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.setting__title {
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
}
|
||||
// 项目配置
|
||||
.setting__content {
|
||||
background: @appBg;
|
||||
padding: 0 20px;
|
||||
|
@ -181,6 +248,10 @@ export default defineComponent({
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 16px 0;
|
||||
align-items: center;
|
||||
&>span {
|
||||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
import { defineComponent, PropType, computed } from 'vue'
|
||||
import { Table } from 'ant-design-vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ComTable',
|
||||
props: {
|
||||
columns: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
setup(props, { attrs, slots }) {
|
||||
const getBindValue = computed((): any => {
|
||||
const bindValue = { ...attrs, ...props }
|
||||
delete bindValue.columns
|
||||
return bindValue
|
||||
})
|
||||
|
||||
function renderTabelItem(columns: any[]) {
|
||||
return columns.map((v: any) => {
|
||||
const vSlots: any = v.slots || {}
|
||||
if (v.children) {
|
||||
const slotData = {
|
||||
title: () => vSlots.title && slots[vSlots.title] && slots[vSlots.title]!(),
|
||||
default: () => {renderTabelItem(v.children)}
|
||||
}
|
||||
if (!vSlots.title) {
|
||||
delete slotData.title
|
||||
}
|
||||
return (
|
||||
<Table.ColumnGroup
|
||||
v-slots={{...slotData}}
|
||||
>
|
||||
</Table.ColumnGroup>
|
||||
)
|
||||
} else {
|
||||
const slotData = {
|
||||
title: () => vSlots.title && slots[vSlots.title] && slots[vSlots.title]!(),
|
||||
default: ({ text, record, index, column }: any) => {
|
||||
if (vSlots.customRender) {
|
||||
return slots[vSlots.customRender] && slots[vSlots.customRender]!({ text, record, index, column })
|
||||
} else {
|
||||
return text
|
||||
}
|
||||
},
|
||||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters, column }: any) => vSlots.filterDropdown && slots[vSlots.filterDropdown] && slots[vSlots.filterDropdown]!({ setSelectedKeys, selectedKeys, confirm, clearFilters, column }),
|
||||
filterIcon: (filtered: any) => vSlots.filterIcon && slots[vSlots.filterIcon] && slots[vSlots.filterIcon]!(filtered)
|
||||
}
|
||||
if (!vSlots.title) {
|
||||
delete slotData.title
|
||||
}
|
||||
if (!vSlots.filterDropdown) {
|
||||
delete slotData.filterDropdown
|
||||
}
|
||||
if (!vSlots.filterIcon) {
|
||||
delete slotData.filterIcon
|
||||
}
|
||||
return (
|
||||
<Table.Column
|
||||
{...v}
|
||||
v-slots={{...slotData}}
|
||||
>
|
||||
</Table.Column>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
const tableSlot = {
|
||||
title: (currentPageData: any) => slots.title && slots.title(currentPageData),
|
||||
footer: (currentPageData: any) => slots.footer && slots.footer(currentPageData),
|
||||
expandedRowRender: ({ record, index, indent, expanded }: any) => slots.expandedRowRender && slots.expandedRowRender({ record, index, indent, expanded }),
|
||||
}
|
||||
if (!slots.title) {
|
||||
delete tableSlot.title
|
||||
}
|
||||
if (!slots.footer) {
|
||||
delete tableSlot.footer
|
||||
}
|
||||
if (!slots.expandedRowRender) {
|
||||
delete tableSlot.expandedRowRender
|
||||
}
|
||||
return (
|
||||
<Table
|
||||
{...(getBindValue as any)}
|
||||
v-slots={tableSlot}
|
||||
>
|
||||
{renderTabelItem(props.columns)}
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1,53 +0,0 @@
|
|||
<template>
|
||||
<template v-if="item.children" />
|
||||
<template v-else>
|
||||
<a-table-column
|
||||
v-bind="getItemBindValue(item)"
|
||||
>
|
||||
<!-- title slot -->
|
||||
<template v-if="item.slots && item.slots.title" #title>
|
||||
<slot :name="item.slots.title" />
|
||||
</template>
|
||||
<!-- default slot -->
|
||||
<template v-if="item.slots && item.slots.customRender" #default="{ text, record, index, column }">
|
||||
<slot :name="item.slots.customRender" :text="text" :record="record" :index="index" :column="column" />
|
||||
</template>
|
||||
<!-- filterDropdown slot -->
|
||||
<template v-if="item.slots && item.slots.filterDropdown" #filterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
|
||||
<slot :name="item.slots.filterDropdown" :setSelectedKeys="setSelectedKeys" :selectedKeys="selectedKeys" :confirm="confirm" :clearFilters="clearFilters" :column="column" />
|
||||
</template>
|
||||
<!-- filterIcon slot -->
|
||||
<template v-if="item.slots && item.slots.filterIcon" #filterIcon="filtered">
|
||||
<slot :name="item.slots.filterIcon" :filtered="filtered" />
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'TableItem',
|
||||
functional: true,
|
||||
props: {
|
||||
item: {
|
||||
type: Object as PropType<object>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props, { attrs }) {
|
||||
alert(',,,,')
|
||||
const getItemBindValue = computed(() => {
|
||||
return function(item: any) {
|
||||
return { ...item, ...attrs, ...props }
|
||||
}
|
||||
})
|
||||
return {
|
||||
getItemBindValue
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,35 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, inject, h, PropType } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'Slot',
|
||||
props: {
|
||||
row: {
|
||||
type: Object as PropType<object>,
|
||||
default: () => null
|
||||
},
|
||||
index: {
|
||||
type: Number as PropType<number>,
|
||||
default: null
|
||||
},
|
||||
column: {
|
||||
type: Object as PropType<object>,
|
||||
default: () => null
|
||||
},
|
||||
slotName: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
render(props: any) {
|
||||
const _this: any = inject('tableRoot')
|
||||
return h('div', _this.slots[props.slotName]({
|
||||
row: props.row,
|
||||
column: props.column,
|
||||
index: props.index
|
||||
}))
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,84 @@
|
|||
<template>
|
||||
<el-table-column v-bind="{...bindValue(child)}" :prop="child.key">
|
||||
<template v-for="item in child.children">
|
||||
<!-- 树型数据 -->
|
||||
<template v-if="item.children && item.children.length">
|
||||
<table-column
|
||||
:key="item[item.key]"
|
||||
:child="item"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-table-column
|
||||
:key="item[item.key]"
|
||||
v-bind="{...bindValue(item)}"
|
||||
:prop="item.key"
|
||||
>
|
||||
<!-- 表头插槽 -->
|
||||
<template v-if="item.slots && item.slots.header" #header="scope">
|
||||
<table-slot
|
||||
v-if="item.slots && item.slots.header"
|
||||
:slot-name="item.slots.header"
|
||||
:row="scope.row"
|
||||
:column="item"
|
||||
:index="scope.$index"
|
||||
/>
|
||||
</template>
|
||||
<!-- 表格内容插槽自定义 -->
|
||||
<template #default="scope">
|
||||
<table-slot
|
||||
v-if="item.slots && item.slots.default"
|
||||
:slot-name="item.slots.default"
|
||||
:row="scope.row"
|
||||
:column="item"
|
||||
:index="scope.$index"
|
||||
/>
|
||||
<!-- 不需要插槽 -->
|
||||
<div v-else style="display: inline-block;">
|
||||
{{ scope.row[item.key] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import TableSlot from './Slot.vue'
|
||||
import { deepClone } from '@/utils'
|
||||
export default defineComponent({
|
||||
name: 'TableColumn',
|
||||
components: {
|
||||
TableSlot
|
||||
},
|
||||
props: {
|
||||
child: {
|
||||
type: Object as PropType<object>,
|
||||
default: () => null,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
function bindValue(item: any) {
|
||||
const delArr: string[] = ['children']
|
||||
const obj = deepClone(item)
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
return {
|
||||
bindValue
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,3 +0,0 @@
|
|||
import Table from './Table'
|
||||
|
||||
export default Table
|
|
@ -1,47 +1,109 @@
|
|||
<template>
|
||||
<a-table v-bind="getBindValue">
|
||||
<table-item
|
||||
v-for="item in columns"
|
||||
:key="item.key || item.dataIndex"
|
||||
:item="item"
|
||||
<el-table ref="elTable" v-bind="getBindValue">
|
||||
<!-- 多选 -->
|
||||
<el-table-column
|
||||
v-if="selection"
|
||||
type="selection"
|
||||
width="55"
|
||||
/>
|
||||
<template v-if="slots.title" #title="currentPageData">
|
||||
<slot name="title" :currentPageData="currentPageData" />
|
||||
</template>
|
||||
<!-- <template v-for="item in columns" :key="item.key || item.dataIndex"> -->
|
||||
<template v-for="item in columns">
|
||||
<!-- 树型数据 -->
|
||||
<template v-if="item.children && item.children.length">
|
||||
<table-column
|
||||
:key="item[item.key]"
|
||||
:child="item"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- </template> -->
|
||||
<template v-if="slots.footer" #footer="currentPageData">
|
||||
<slot name="footer" :currentPageData="currentPageData" />
|
||||
<template v-else>
|
||||
<el-table-column
|
||||
:key="item[item.key]"
|
||||
v-bind="{...bindValue(item)}"
|
||||
:prop="item.key"
|
||||
>
|
||||
<!-- 表头插槽 -->
|
||||
<template v-if="item.slots && item.slots.header" #header="scope">
|
||||
<table-slot
|
||||
v-if="item.slots && item.slots.header"
|
||||
:slot-name="item.slots.header"
|
||||
:row="scope.row"
|
||||
:column="item"
|
||||
:index="scope.$index"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 表格内容插槽自定义 -->
|
||||
<template #default="scope">
|
||||
<table-slot
|
||||
v-if="item.slots && item.slots.default"
|
||||
:slot-name="item.slots.default"
|
||||
:row="scope.row"
|
||||
:column="item"
|
||||
:index="scope.$index"
|
||||
/>
|
||||
<!-- 不需要插槽 -->
|
||||
<div v-else style="display: inline-block;">
|
||||
{{ scope.row[item.key] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed } from 'vue'
|
||||
import TableItem from './TableItem.vue'
|
||||
import { defineComponent, PropType, computed, provide, getCurrentInstance, ref, unref } from 'vue'
|
||||
import { deepClone } from '@/utils'
|
||||
import TableColumn from './components/TableColumn.vue'
|
||||
import TableSlot from './components/Slot.vue'
|
||||
export default defineComponent({
|
||||
name: 'ComTable',
|
||||
components: {
|
||||
TableItem
|
||||
TableSlot,
|
||||
TableColumn
|
||||
},
|
||||
props: {
|
||||
columns: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: () => []
|
||||
},
|
||||
selection: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, { attrs, slots }) {
|
||||
console.log(TableItem)
|
||||
const elTable = ref<HTMLElement | null>(null)
|
||||
function getTableRef() {
|
||||
return unref(elTable as any)
|
||||
}
|
||||
|
||||
const _this = getCurrentInstance() as any
|
||||
provide('tableRoot', _this)
|
||||
|
||||
const getBindValue = computed((): any => {
|
||||
const bindValue = { ...attrs, ...props }
|
||||
delete bindValue.columns
|
||||
return bindValue
|
||||
})
|
||||
|
||||
function bindValue(item: any) {
|
||||
const delArr: string[] = []
|
||||
const obj = deepClone(item)
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
return {
|
||||
getBindValue,
|
||||
slots
|
||||
elTable,
|
||||
getBindValue, bindValue,
|
||||
slots,
|
||||
getTableRef
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Clipboard from 'clipboard'
|
||||
import { Directive, DirectiveBinding } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
if (!Clipboard) {
|
||||
throw new Error('you should npm install `clipboard` --save at first ')
|
||||
|
@ -47,7 +47,7 @@ function createdClipboard(el: HTMLElement | any, arg: string | undefined, value:
|
|||
if (callback) {
|
||||
callback(e)
|
||||
} else {
|
||||
message.success('复制成功')
|
||||
ElMessage.success('复制成功')
|
||||
}
|
||||
})
|
||||
clipboard.on('error', e => {
|
||||
|
@ -55,7 +55,7 @@ function createdClipboard(el: HTMLElement | any, arg: string | undefined, value:
|
|||
if (callback) {
|
||||
callback(e)
|
||||
} else {
|
||||
message.success('复制失败')
|
||||
ElMessage.success('复制失败')
|
||||
}
|
||||
})
|
||||
el._v_clipboard = clipboard
|
||||
|
|
|
@ -92,6 +92,12 @@ import {
|
|||
ElNotification
|
||||
} from 'element-plus'
|
||||
|
||||
import locale from 'element-plus/lib/locale'
|
||||
import lang from 'element-plus/lib/locale/lang/zh-cn'
|
||||
|
||||
// 设置语言
|
||||
locale.use(lang)
|
||||
|
||||
const components = [
|
||||
ElAlert,
|
||||
ElAside,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import request from './request'
|
||||
|
||||
import config from '../config'
|
||||
import config from './config'
|
||||
|
||||
import { AxiosPromise, ResponseType } from 'axios'
|
||||
|
||||
|
|
|
@ -1,39 +1,9 @@
|
|||
/**
|
||||
* 全局配置
|
||||
* request全局配置
|
||||
*/
|
||||
import { ConfigOptions } from './types'
|
||||
|
||||
const config: ConfigOptions = {
|
||||
/**
|
||||
* 配置显示在浏览器标签的title
|
||||
*/
|
||||
title: 'vue-antdv-admin',
|
||||
|
||||
/**
|
||||
* 是否显示标签页
|
||||
*/
|
||||
has_tags: true,
|
||||
|
||||
/**
|
||||
* 是否显示logo
|
||||
*/
|
||||
show_logo: true,
|
||||
|
||||
/**
|
||||
* logo标题
|
||||
*/
|
||||
logo_title: 'vue-antdv-admin',
|
||||
|
||||
/**
|
||||
* 横纵布局 Classic(经典) Top(头部) LeftTop(左侧顶部)
|
||||
*/
|
||||
layout: 'Test',
|
||||
|
||||
/**
|
||||
* 主题色 light(明亮) dark(暗黑)
|
||||
*/
|
||||
theme: 'dark',
|
||||
|
||||
/**
|
||||
* api请求基础路径
|
||||
*/
|
||||
|
@ -61,12 +31,7 @@ const config: ConfigOptions = {
|
|||
* 默认接口请求类型
|
||||
* 可选值:application/x-www-form-urlencoded multipart/form-data
|
||||
*/
|
||||
default_headers: 'application/json',
|
||||
|
||||
/**
|
||||
* 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突
|
||||
*/
|
||||
user_info: 'userInfo'
|
||||
default_headers: 'application/json'
|
||||
}
|
||||
|
||||
export default config
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* request配置
|
||||
*/
|
||||
export interface ConfigOptions {
|
||||
base_url: object
|
||||
result_code: number | string
|
||||
default_headers: string
|
||||
request_timeout: number
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
|
||||
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import qs from 'qs'
|
||||
|
||||
import config from '../config'
|
||||
import config from './config'
|
||||
|
||||
const { result_code, base_url } = config
|
||||
|
||||
|
@ -37,12 +37,12 @@ service.interceptors.response.use(
|
|||
if (response.data.code === result_code) {
|
||||
return response.data
|
||||
} else {
|
||||
message.error(response.data.message)
|
||||
ElMessage.error(response.data.message)
|
||||
}
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log('err' + error) // for debug
|
||||
message.error(error.message)
|
||||
ElMessage.error(error.message)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
/**
|
||||
* 全局配置
|
||||
*/
|
||||
export interface ConfigOptions {
|
||||
title?: string
|
||||
has_tags: boolean
|
||||
show_logo: boolean
|
||||
logo_title: string
|
||||
base_url: object
|
||||
result_code: number | string
|
||||
default_headers: string
|
||||
request_timeout: number
|
||||
user_info: string
|
||||
layout: 'Classic' | 'Top' | 'LeftTop' | 'Test'
|
||||
theme: 'light' | 'dark'
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
<template>
|
||||
<router-link :class="['app-logo', 'app-logo-' + theme]" to="/">
|
||||
<img :src="require('@/assets/img/logo.png')">
|
||||
<div v-if="show" class="sidebar-title">{{ title }}</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch, PropType } from 'vue'
|
||||
import config from '_p/index/config'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Logo',
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
required: true
|
||||
},
|
||||
theme: {
|
||||
type: String as PropType<'light' | 'dark'>,
|
||||
default: 'dark'
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const show = ref<boolean>(true)
|
||||
watch(
|
||||
() => props.collapsed,
|
||||
(collapsed: boolean) => {
|
||||
if (!collapsed) {
|
||||
setTimeout(() => {
|
||||
show.value = !collapsed
|
||||
}, 400)
|
||||
} else {
|
||||
show.value = !collapsed
|
||||
}
|
||||
}
|
||||
)
|
||||
return {
|
||||
show,
|
||||
title: config.title
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.app-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 18px;
|
||||
cursor: pointer;
|
||||
height: @topSilderHeight;
|
||||
max-width: 200px;
|
||||
img {
|
||||
width: 37px;
|
||||
height: 37px;
|
||||
}
|
||||
.sidebar-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
transition: .5s;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
.app-logo-dark {
|
||||
background-color: @menuBg;
|
||||
.sidebar-title {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.app-logo-light {
|
||||
background-color: #fff;
|
||||
.sidebar-title {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,75 +0,0 @@
|
|||
<template>
|
||||
<div class="navbar">
|
||||
<hamburger :collapsed="collapsed" class="hamburger-container" @toggleClick="setCollapsed" />
|
||||
<breadcrumb class="breadcrumb-container" />
|
||||
|
||||
<div class="right-menu">
|
||||
<screenfull class="right-menu-item hover-effect" />
|
||||
|
||||
<user-info class="right-menu-item hover-effect" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import Hamburger from '_c/Hamburger/index.vue'
|
||||
import Breadcrumb from '_c/Breadcrumb/index.vue'
|
||||
import Screenfull from '_c/Screenfull/index.vue'
|
||||
import UserInfo from './UserInfo.vue'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
export default defineComponent({
|
||||
name: 'Navbar',
|
||||
components: {
|
||||
Hamburger,
|
||||
Breadcrumb,
|
||||
Screenfull,
|
||||
UserInfo
|
||||
},
|
||||
setup() {
|
||||
const collapsed = computed(() => appStore.collapsed)
|
||||
|
||||
function setCollapsed(collapsed: boolean): void {
|
||||
appStore.SetCollapsed(collapsed)
|
||||
}
|
||||
|
||||
return {
|
||||
collapsed,
|
||||
setCollapsed
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.navbar {
|
||||
.hamburger-container {
|
||||
line-height: @navbarHeight;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
margin-left: 15px;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, .025);
|
||||
}
|
||||
}
|
||||
.breadcrumb-container {
|
||||
float: left;
|
||||
}
|
||||
.right-menu {
|
||||
float: right;
|
||||
height: 100%;
|
||||
line-height: @navbarHeight;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
.right-menu-item {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
height: 100%;
|
||||
font-size: 18px;
|
||||
color: #5a5e66;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,34 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<i v-if="icon.includes('el-icon')" :class="[icon, 'sub-el-icon', 'anticon']" />
|
||||
<svg-icon v-else :icon-class="icon" class="anticon" />
|
||||
<slot name="title">
|
||||
<span class="anticon-item">{{ title }}</span>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Item',
|
||||
props: {
|
||||
icon: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.anticon-item {
|
||||
opacity: 1;
|
||||
transition: opacity .3s cubic-bezier(.645,.045,.355,1),width .3s cubic-bezier(.645,.045,.355,1);
|
||||
}
|
||||
</style>
|
|
@ -1,92 +0,0 @@
|
|||
import { ref } from 'vue'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import path from 'path'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
// import { setSidebarItem } from './types'
|
||||
|
||||
export function setSidebarItem() {
|
||||
const onlyOneChild = ref<any>(null)
|
||||
|
||||
function hasOneShowingChild(children: RouteRecordRaw[] = [], parent: RouteRecordRaw): boolean {
|
||||
const showingChildren: RouteRecordRaw[] = children.filter((item: RouteRecordRaw) => {
|
||||
if (item.meta && item.meta.hidden) {
|
||||
return false
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
onlyOneChild.value = item
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Show parent if there are no child router to display
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function resolvePath(basePath: string, routePath: string): string {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath
|
||||
}
|
||||
return path.resolve(basePath, routePath)
|
||||
}
|
||||
|
||||
function treeFindRouter(tree: any[], func: Function, result: RouteRecordRaw[] = []): RouteRecordRaw[] {
|
||||
if (!tree) return []
|
||||
for (const data of tree) {
|
||||
result.push(data)
|
||||
if (func(data)) return result
|
||||
if (data.children) {
|
||||
const findChildren = treeFindRouter(data.children, func, result)
|
||||
if (findChildren.length) return findChildren
|
||||
}
|
||||
result.pop()
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function getFullPath(arr: string[]): string[] {
|
||||
const result: string[] = []
|
||||
let basePath = '/'
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (i === arr.length) {
|
||||
continue
|
||||
}
|
||||
result.push(resolvePath(basePath, arr[i]))
|
||||
basePath = resolvePath(basePath, arr[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function findCurrentRoute(routers: RouteRecordRaw[], path: string, basePath = '/', result: Array<any> = []): any {
|
||||
for (const item of routers) {
|
||||
if (!item.meta?.hidden) {
|
||||
const _basePath = resolvePath(basePath, item.path)
|
||||
if (_basePath === path && !item.children) {
|
||||
result.push(item)
|
||||
} else {
|
||||
if (item.children) {
|
||||
findCurrentRoute(item.children, path, _basePath, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result ? result[0] : null
|
||||
}
|
||||
|
||||
return {
|
||||
onlyOneChild,
|
||||
hasOneShowingChild,
|
||||
resolvePath,
|
||||
treeFindRouter,
|
||||
getFullPath,
|
||||
findCurrentRoute
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import type { Ref } from 'vue'
|
||||
import ref from 'vue'
|
||||
|
||||
export interface setSidebarItem = {
|
||||
onlyOneChild: Ref<any | null>
|
||||
hasOneShowingChild: Function<boolean>
|
||||
resolvePath: Function<string>
|
||||
treeFindPath: Function<string[]>
|
||||
getFullPath: Function<string[]>
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
.sidebar-container {
|
||||
height: 100%;
|
||||
}
|
||||
.has-logo {
|
||||
height: calc(~"100% - @{topSilderHeight}");
|
||||
}
|
||||
.menu-wrap {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
import Silder from './index'
|
||||
|
||||
export default Silder
|
|
@ -1,176 +0,0 @@
|
|||
import { ref, defineComponent, PropType, computed, watch } from 'vue'
|
||||
// import { Menu } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { RouteRecordRaw, RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import Scrollbar from '_c/Scrollbar/index.vue'
|
||||
import Item from './Item.vue'
|
||||
import { permissionStore } from '_p/index/store/modules/permission'
|
||||
import { setSidebarItem } from './hooks/setSidebarItem'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import { findIndex } from '@/utils'
|
||||
import config from '_p/index/config'
|
||||
const { show_logo } = config
|
||||
|
||||
import './index.less'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Silder',
|
||||
components: {
|
||||
Scrollbar,
|
||||
Item
|
||||
},
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true
|
||||
},
|
||||
inlineIndent: {
|
||||
type: Number as PropType<number>,
|
||||
default: 24
|
||||
},
|
||||
forceSubMenuRender: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
mode: {
|
||||
type: String as PropType<'vertical' | 'horizontal' | 'vertical-right' | 'inline'>,
|
||||
default: 'inline'
|
||||
},
|
||||
theme: {
|
||||
type: String as PropType<'light' | 'dark'>,
|
||||
default: 'dark'
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { currentRoute, push } = useRouter()
|
||||
const { resolvePath, treeFindRouter, getFullPath, findCurrentRoute } = setSidebarItem()
|
||||
const routers = computed((): RouteRecordRaw[] => {
|
||||
return permissionStore.routers
|
||||
})
|
||||
const selectedKeys = ref<string[]>([])
|
||||
const openKeys = ref<string[]>([])
|
||||
const activeMenuName = ref<string>('')
|
||||
|
||||
watch(
|
||||
() => currentRoute.value,
|
||||
(route: RouteLocationNormalizedLoaded) => {
|
||||
setSelectedKeys(route)
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.collapsed,
|
||||
(collapsed: boolean) => {
|
||||
setOpenKeys(currentRoute.value, collapsed)
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
function handleOpenChange(keyArr: string[]) {
|
||||
// 需要自己管理openKeys
|
||||
openKeys.value = keyArr
|
||||
}
|
||||
|
||||
function setOpenKeys(route: RouteLocationNormalizedLoaded, collapsed: boolean) {
|
||||
let currentRoute: any = null
|
||||
if (route.meta.activeMenu) {
|
||||
currentRoute = findCurrentRoute(routers.value, route.meta.activeMenu)
|
||||
} else {
|
||||
currentRoute = route
|
||||
}
|
||||
const parentRoute: RouteRecordRaw[] = treeFindRouter(permissionStore.routers, (data: any) => data.name === currentRoute.name)
|
||||
openKeys.value = collapsed ? [] : getFullPath(parentRoute.map((v: RouteRecordRaw) => v.path))
|
||||
}
|
||||
|
||||
function setSelectedKeys(route: RouteLocationNormalizedLoaded) {
|
||||
const { meta, path, name } = route
|
||||
activeMenuName.value = name as string
|
||||
if (meta.activeMenu) {
|
||||
selectedKeys.value = [meta.activeMenu]
|
||||
return
|
||||
}
|
||||
selectedKeys.value = [path]
|
||||
}
|
||||
|
||||
function highlightMenu(className: string, basePath: string) {
|
||||
const parentRoute: RouteRecordRaw[] = treeFindRouter(permissionStore.routers, (data: any) => data.name === currentRoute.value.name)
|
||||
const parentFullPath: string[] = getFullPath(parentRoute.map((v: RouteRecordRaw) => v.path))
|
||||
return findIndex(parentFullPath, (item: string) => item === basePath) !== -1 ? className : ''
|
||||
}
|
||||
|
||||
function handleMenuClick({ key }: any) {
|
||||
console.log(key)
|
||||
if (isExternal(key)) {
|
||||
window.open(key)
|
||||
} else {
|
||||
push({ path: key })
|
||||
}
|
||||
}
|
||||
|
||||
// function renderMenu(routers: RouteRecordRaw[], basePath = '/') {
|
||||
// if (routers && routers.length) {
|
||||
// return routers.map((item: RouteRecordRaw) => {
|
||||
// if (!item.meta?.hidden) {
|
||||
// const _basePath = resolvePath(basePath, item.path)
|
||||
// const { onlyOneChild, hasOneShowingChild } = setSidebarItem()
|
||||
// if (hasOneShowingChild(item.children, item) && (!onlyOneChild.value.children || onlyOneChild.value.noShowingChildren) && !item.meta?.alwaysShow) {
|
||||
// return (
|
||||
// <Menu.Item key={resolvePath(_basePath, onlyOneChild.value.path)} v-slots={{
|
||||
// default: () => [
|
||||
// <Item
|
||||
// icon={onlyOneChild.value.meta && onlyOneChild.value.meta.icon}
|
||||
// title={onlyOneChild.value.meta?.title}
|
||||
// />
|
||||
// ]
|
||||
// }}>
|
||||
// </Menu.Item>
|
||||
// )
|
||||
// } else {
|
||||
// return (
|
||||
// <Menu.SubMenu
|
||||
// key={_basePath}
|
||||
// class={highlightMenu(props.theme + '-active-item', _basePath)}
|
||||
// popupClassName={highlightMenu(props.theme + '-popup-active-item', _basePath)}
|
||||
// v-slots={{
|
||||
// title: () => [
|
||||
// <Item
|
||||
// icon={item.meta && item.meta.icon}
|
||||
// title={item.meta?.title}
|
||||
// />
|
||||
// ]
|
||||
// }}
|
||||
// >
|
||||
// {renderMenu(item.children as any, _basePath)}
|
||||
// </Menu.SubMenu>
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// return () => {
|
||||
// return (
|
||||
// // <div class={{ 'has-logo': show_logo, 'sidebar-container': true }}>
|
||||
// // <scrollbar class='menu-wrap'>
|
||||
// // <Menu
|
||||
// // selectedKeys={selectedKeys.value}
|
||||
// // openKeys={openKeys.value}
|
||||
// // theme={props.theme}
|
||||
// // mode={props.mode}
|
||||
// // onClick={handleMenuClick}
|
||||
// // onOpenChange={handleOpenChange}
|
||||
// // >
|
||||
// // {renderMenu(routers.value)}
|
||||
// // </Menu>
|
||||
// // </scrollbar>
|
||||
// // </div>
|
||||
// )
|
||||
// }
|
||||
}
|
||||
})
|
|
@ -1,377 +0,0 @@
|
|||
<template>
|
||||
<div ref="wrapper" class="tags-view-container">
|
||||
<span class="move-btn prev-btn">
|
||||
<a-button @click="move(-200)">
|
||||
<template #icon><LeftOutlined /></template>
|
||||
</a-button>
|
||||
</span>
|
||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
|
||||
<div>
|
||||
<router-link
|
||||
v-for="tag in visitedViews"
|
||||
:ref="setTagRef"
|
||||
:key="tag.path"
|
||||
:class="isActive(tag) ? 'active' : ''"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
tag="span"
|
||||
class="tags-view-item"
|
||||
@click.middle="closeSelectedTag(tag)"
|
||||
@contextmenu.prevent="openMenu(tag, $event)"
|
||||
>
|
||||
{{ tag.title }}
|
||||
<CloseOutlined v-if="!tag.meta.affix" class="icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
|
||||
</router-link>
|
||||
</div>
|
||||
</scroll-pane>
|
||||
<span class="move-btn next-btn">
|
||||
<a-button @click="move(200)">
|
||||
<template #icon><RightOutlined /></template>
|
||||
</a-button>
|
||||
</span>
|
||||
<ul v-show="visible" :style="{left: left + 'px', top: top + 'px'}" class="contextmenu">
|
||||
<li @click="refreshSelectedTag(selectedTag)">
|
||||
刷新
|
||||
</li>
|
||||
<li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">
|
||||
关闭
|
||||
</li>
|
||||
<li @click="closeOthersTags">
|
||||
关闭其他
|
||||
</li>
|
||||
<li @click="closeAllTags(selectedTag)">
|
||||
关闭全部
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import ScrollPane from '_c/ScrollPane/index.vue'
|
||||
import path from 'path'
|
||||
import { defineComponent, ref, unref, computed, watch, onMounted, nextTick } from 'vue'
|
||||
import { tagsViewStore } from '_p/index/store/modules/tagsView'
|
||||
import { permissionStore } from '_p/index/store/modules/permission'
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
|
||||
// import { LeftOutlined, RightOutlined, CloseOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TagsView',
|
||||
components: {
|
||||
ScrollPane,
|
||||
// CloseOutlined,
|
||||
// LeftOutlined,
|
||||
// RightOutlined
|
||||
},
|
||||
setup() {
|
||||
const { currentRoute, push, replace } = useRouter()
|
||||
const wrapper = ref<HTMLElement | null>(null)
|
||||
const scrollPane = ref<HTMLElement | null>(null)
|
||||
const visible = ref<boolean>(false)
|
||||
const top = ref<number>(0)
|
||||
const left = ref<number>(0)
|
||||
const selectedTag = ref<any>({})
|
||||
const affixTags = ref<any[]>([])
|
||||
const visitedViews = computed(() => tagsViewStore.visitedViews)
|
||||
const routers = computed(() => permissionStore.routers)
|
||||
|
||||
const tagRefs = ref<any[]>([])
|
||||
|
||||
function setTagRef(el: any): void {
|
||||
tagRefs.value.push(el)
|
||||
}
|
||||
|
||||
function isActive(route: RouteLocationNormalizedLoaded): boolean {
|
||||
return route.path === currentRoute.value.path
|
||||
}
|
||||
|
||||
function filterAffixTags(routes: RouteRecordRaw[], basePath = '/'): any[] {
|
||||
let tags: any[] = []
|
||||
routes.forEach((route: RouteRecordRaw) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path)
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta }
|
||||
})
|
||||
}
|
||||
if (route.children) {
|
||||
const tempTags: any[] = filterAffixTags(route.children, route.path)
|
||||
if (tempTags.length >= 1) {
|
||||
tags = [...tags, ...tempTags]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
function initTags(): void {
|
||||
affixTags.value = filterAffixTags(routers.value)
|
||||
const affixTagArr: any[] = affixTags.value
|
||||
for (const tag of affixTagArr) {
|
||||
// Must have tag name
|
||||
if (tag.name) {
|
||||
tagsViewStore.addVisitedView(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addTags(): void | boolean {
|
||||
const { name } = currentRoute.value
|
||||
if (name) {
|
||||
tagsViewStore.addView(currentRoute.value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function moveToCurrentTag() {
|
||||
// TODO 要手动清除tagRefs,不然会一直重复,后续看看有没有什么解决办法
|
||||
tagRefs.value = []
|
||||
const tags = unref(tagRefs)
|
||||
nextTick(() => {
|
||||
for (const tag of tags) {
|
||||
if (tag && tag.to.path === currentRoute.value.path) {
|
||||
(unref(scrollPane) as any).moveToTarget(tag)
|
||||
// when query is different then update
|
||||
if (tag.to.fullPath !== currentRoute.value.fullPath) {
|
||||
tagsViewStore.updateVisitedView(currentRoute.value)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function refreshSelectedTag(view: RouteLocationNormalizedLoaded) {
|
||||
await tagsViewStore.delCachedView()
|
||||
const { fullPath } = view
|
||||
nextTick(() => {
|
||||
replace({
|
||||
path: '/redirect' + fullPath
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function closeSelectedTag(view: RouteLocationNormalizedLoaded) {
|
||||
const views: any = await tagsViewStore.delView(view)
|
||||
if (isActive(view)) {
|
||||
toLastView(views.visitedViews)
|
||||
}
|
||||
}
|
||||
|
||||
function closeOthersTags() {
|
||||
push(selectedTag.value)
|
||||
tagsViewStore.delOthersViews(selectedTag.value).then(() => {
|
||||
moveToCurrentTag()
|
||||
})
|
||||
}
|
||||
|
||||
async function closeAllTags(view: RouteLocationNormalizedLoaded) {
|
||||
const views: any = await tagsViewStore.delAllViews()
|
||||
if (affixTags.value.some(tag => tag.path === view.path)) {
|
||||
return
|
||||
}
|
||||
toLastView(views.visitedViews)
|
||||
}
|
||||
|
||||
function toLastView(visitedViews: RouteLocationNormalizedLoaded[]) {
|
||||
const latestView = visitedViews.slice(-1)[0]
|
||||
setTimeout(() => {
|
||||
if (latestView) {
|
||||
push(latestView)
|
||||
} else {
|
||||
// You can set another route
|
||||
push('/')
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
function openMenu(tag: RouteLocationNormalizedLoaded, e: any) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft: number = (unref(wrapper) as any).getBoundingClientRect().left // container margin left
|
||||
const offsetWidth: number = (unref(wrapper) as any).offsetWidth // container width
|
||||
const maxLeft: number = offsetWidth - menuMinWidth// left boundary
|
||||
const itemLeft: number = e.clientX - offsetLeft + 4
|
||||
|
||||
if (itemLeft > maxLeft) {
|
||||
left.value = maxLeft
|
||||
} else {
|
||||
left.value = itemLeft
|
||||
}
|
||||
top.value = e.offsetY
|
||||
|
||||
visible.value = true
|
||||
selectedTag.value = tag
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
function move(to: number) {
|
||||
(unref(scrollPane) as any).moveTo(to)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initTags()
|
||||
addTags()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => currentRoute.value,
|
||||
() => {
|
||||
addTags()
|
||||
moveToCurrentTag()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => visible.value,
|
||||
(visible: boolean) => {
|
||||
if (visible) {
|
||||
document.body.addEventListener('click', closeMenu)
|
||||
} else {
|
||||
document.body.removeEventListener('click', closeMenu)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
wrapper, scrollPane,
|
||||
visible, top, left,
|
||||
selectedTag, affixTags,
|
||||
visitedViews, routers,
|
||||
tagRefs, setTagRef,
|
||||
isActive,
|
||||
filterAffixTags,
|
||||
initTags,
|
||||
addTags,
|
||||
moveToCurrentTag,
|
||||
refreshSelectedTag,
|
||||
closeSelectedTag,
|
||||
closeOthersTags,
|
||||
closeAllTags,
|
||||
toLastView,
|
||||
openMenu,
|
||||
closeMenu,
|
||||
move
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.tags-view-container {
|
||||
height: @tagsViewHeight;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
position: relative;
|
||||
display: flex;
|
||||
z-index: 100;
|
||||
&::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border-top: #d8dce5;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.move-btn {
|
||||
display: inline-block;
|
||||
width: @tagsViewHeight;
|
||||
height: @tagsViewHeight;
|
||||
line-height: @tagsViewHeight;
|
||||
text-align: center;
|
||||
.ant-btn {
|
||||
width: @tagsViewHeight;
|
||||
height: @tagsViewHeight;
|
||||
line-height: @tagsViewHeight;
|
||||
}
|
||||
}
|
||||
.tags-view-wrapper {
|
||||
width: calc(~"100% - 78px");
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
&:last-of-type {
|
||||
margin-right: 4px;
|
||||
}
|
||||
&.active {
|
||||
background-color: #304156;
|
||||
color: #fff;
|
||||
border-color: #304156;
|
||||
&::before {
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.icon-close {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 16px;
|
||||
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
||||
transform-origin: 100% 50%;
|
||||
margin-left: 5px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
&:before {
|
||||
transform: scale(.6);
|
||||
display: inline-block;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #b4bccc;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
z-index: 200;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@{deep}(.scrollbar__view) {
|
||||
height: @tagsViewHeight;
|
||||
line-height: @tagsViewHeight;
|
||||
}
|
||||
</style>
|
|
@ -1,68 +0,0 @@
|
|||
<template>
|
||||
<el-dropdown class="avatar-container" :trigger="['hover']">
|
||||
<div>
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="require('@/assets/img/avatar.gif')" class="user-avatar">
|
||||
<span class="name-item">管理员</span>
|
||||
</div>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item key="1">
|
||||
<router-link to="/">
|
||||
首页
|
||||
</router-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item key="2">
|
||||
<span style="display: block;" @click="loginOut">退出登录</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { resetRouter } from '_p/index/router'
|
||||
import wsCache from '@/cache'
|
||||
import { useRouter } from 'vue-router'
|
||||
export default defineComponent({
|
||||
name: 'UserInfo',
|
||||
setup() {
|
||||
const { replace } = useRouter()
|
||||
async function loginOut(): Promise<void> {
|
||||
wsCache.clear()
|
||||
await resetRouter() // 重置静态路由表
|
||||
// this.$store.dispatch('delAllViews') // 删除所有的tags标签页
|
||||
replace('/login')
|
||||
}
|
||||
return {
|
||||
loginOut
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.avatar-container {
|
||||
margin-right: 30px;
|
||||
padding: 0 10px;
|
||||
.avatar-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
.user-avatar {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.name-item {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,29 +1,26 @@
|
|||
<template>
|
||||
<div class="app-wrapper">
|
||||
<component :is="component" />
|
||||
<component :is="layout" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
// import Classic from './modules/Classic.vue'
|
||||
// import Top from './modules/Top.vue'
|
||||
// import LeftTop from './modules/LeftTop.vue'
|
||||
import Test from './modules/Test.vue'
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import config from '_p/index/config'
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import Classic from './modules/Classic.vue'
|
||||
import Top from './modules/Top.vue'
|
||||
import LeftTop from './modules/LeftTop.vue'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
export default defineComponent({
|
||||
name: 'Layout',
|
||||
components: {
|
||||
// Classic,
|
||||
// Top,
|
||||
// LeftTop,
|
||||
Test
|
||||
Classic,
|
||||
Top,
|
||||
LeftTop
|
||||
},
|
||||
setup() {
|
||||
const { layout } = config
|
||||
const component = ref<string>(layout)
|
||||
const layout = computed(() => appStore.layout)
|
||||
return {
|
||||
component
|
||||
layout
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,112 +1,251 @@
|
|||
<template>
|
||||
<a-layout class="app-wrapper">
|
||||
<a-layout-sider
|
||||
v-model:collapsed="collapsed"
|
||||
:trigger="null"
|
||||
collapsible
|
||||
:class="'ant-layout-sider--' + theme"
|
||||
class="sidebar-container-wrap"
|
||||
<div :class="classObj" class="app__wrap">
|
||||
<!-- Classic -->
|
||||
<div
|
||||
class="sidebar__wrap"
|
||||
:class="{'sidebar__wrap--collapsed': collapsed}"
|
||||
>
|
||||
<logo
|
||||
v-if="show_logo"
|
||||
v-if="showLogo && layout === 'Classic'"
|
||||
:collapsed="collapsed"
|
||||
:theme="theme"
|
||||
/>
|
||||
<silder
|
||||
:collapsed="collapsed"
|
||||
:theme="theme"
|
||||
/>
|
||||
</a-layout-sider>
|
||||
<a-layout>
|
||||
<a-layout-header :class="{'ant-layout-header-collapsed': collapsed}">
|
||||
<navbar />
|
||||
</a-layout-header>
|
||||
<a-layout-content :class="{'layout-content-has-tags':has_tags}">
|
||||
<scrollbar class="main-wrap">
|
||||
<tags-view :class="{'has-tags':has_tags, 'has-tags-collapsed': collapsed && has_tags}" />
|
||||
<app-main class="classic-module--wrap" />
|
||||
</scrollbar>
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
<sider :layout="layout" mode="vertical" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="main__wrap"
|
||||
:class="{
|
||||
'main__wrap--collapsed': collapsed
|
||||
}"
|
||||
>
|
||||
<el-scrollbar
|
||||
class="main__wrap--content"
|
||||
:class="{
|
||||
'main__wrap--fixed--all': fixedHeader && showNavbar && showTags,
|
||||
'main__wrap--fixed--nav': fixedHeader && showNavbar && !showTags,
|
||||
'main__wrap--fixed--tags': fixedHeader && !showNavbar && showTags
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="header__wrap"
|
||||
:class="{
|
||||
'header__wrap--fixed': fixedHeader,
|
||||
'header__wrap--collapsed': fixedHeader && collapsed
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="showNavbar"
|
||||
class="navbar__wrap"
|
||||
>
|
||||
<hamburger
|
||||
v-if="showHamburger"
|
||||
:collapsed="collapsed"
|
||||
class="hover-container"
|
||||
@toggleClick="setCollapsed"
|
||||
/>
|
||||
<breadcrumb v-if="showBreadcrumb" />
|
||||
<div v-if="showScreenfull || showUserInfo" class="navbar__wrap--right">
|
||||
<screenfull v-if="showScreenfull" class="hover-container screenfull-container" />
|
||||
<user-info v-if="showUserInfo" class="hover-container user-container" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showTags"
|
||||
class="tags__wrap"
|
||||
>
|
||||
<tags-view />
|
||||
</div>
|
||||
</div>
|
||||
<app-main />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<!-- setting -->
|
||||
<setting />
|
||||
<!-- setting -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, PropType } from 'vue'
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
import Silder from '../components/Silder'
|
||||
import Navbar from '../components/Navbar.vue'
|
||||
|
||||
import AppMain from '../components/AppMain.vue'
|
||||
import TagsView from '../components/TagsView.vue'
|
||||
import Logo from '../components/Logo.vue'
|
||||
import Scrollbar from '_c/Scrollbar/index.vue'
|
||||
import config from '_p/index/config'
|
||||
const { show_logo, has_tags, theme } = config
|
||||
import TagsView from '_c/TagsView/index.vue'
|
||||
import Logo from '_c/Logo/index.vue'
|
||||
import Sider from '_c/Sider/index.vue'
|
||||
import Hamburger from '_c/Hamburger/index.vue'
|
||||
import Breadcrumb from '_c/Breadcrumb/index.vue'
|
||||
import Screenfull from '_c/Screenfull/index.vue'
|
||||
import UserInfo from '_c/UserInfo/index.vue'
|
||||
|
||||
import Setting from '_c/Setting/index.vue'
|
||||
export default defineComponent({
|
||||
name: 'Classic',
|
||||
components: {
|
||||
Silder,
|
||||
Navbar,
|
||||
Sider,
|
||||
Hamburger,
|
||||
Breadcrumb,
|
||||
Screenfull,
|
||||
UserInfo,
|
||||
AppMain,
|
||||
TagsView,
|
||||
Logo,
|
||||
Scrollbar
|
||||
},
|
||||
props: {
|
||||
theme: {
|
||||
type: String as PropType<'light' | 'dark'>,
|
||||
default: theme
|
||||
}
|
||||
Setting
|
||||
},
|
||||
setup() {
|
||||
const layout = computed(() => appStore.layout)
|
||||
const collapsed = computed(() => appStore.collapsed)
|
||||
const showLogo = computed(() => appStore.showLogo)
|
||||
const showTags = computed(() => appStore.showTags)
|
||||
const showBreadcrumb = computed(() => appStore.showBreadcrumb)
|
||||
const showHamburger = computed(() => appStore.showHamburger)
|
||||
const showScreenfull = computed(() => appStore.showScreenfull)
|
||||
const showUserInfo = computed(() => appStore.showUserInfo)
|
||||
const showNavbar = computed(() => appStore.showNavbar)
|
||||
// const fixedNavbar = computed(() => appStore.fixedNavbar)
|
||||
// const fixedTags = computed(() => appStore.fixedTags)
|
||||
const fixedHeader = computed(() => appStore.fixedHeader)
|
||||
|
||||
const classObj = computed(() => {
|
||||
const obj = {}
|
||||
obj[`app__wrap--${layout.value}`] = true
|
||||
return obj
|
||||
})
|
||||
|
||||
function setCollapsed(collapsed: boolean): void {
|
||||
appStore.SetCollapsed(collapsed)
|
||||
}
|
||||
|
||||
return {
|
||||
classObj,
|
||||
layout,
|
||||
collapsed,
|
||||
show_logo,
|
||||
has_tags
|
||||
showLogo,
|
||||
showTags,
|
||||
showBreadcrumb,
|
||||
showHamburger,
|
||||
showScreenfull,
|
||||
showUserInfo,
|
||||
showNavbar,
|
||||
fixedHeader,
|
||||
// fixedNavbar,
|
||||
// fixedTags,
|
||||
setCollapsed
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@{deep}(.ant-layout-header) {
|
||||
line-height: @navbarHeight;
|
||||
height: @navbarHeight;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: @menuWidth;
|
||||
width: calc(~"100% - @{menuWidth}");
|
||||
padding: 0;
|
||||
background: #fff;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.ant-layout-sider--light {
|
||||
background: @menuLightBg;
|
||||
}
|
||||
.ant-layout-sider--dark {
|
||||
background: @menuBg;
|
||||
}
|
||||
.ant-layout-content {
|
||||
margin-top: @navbarHeight;
|
||||
.mian-wrap {
|
||||
background-color: @contentBg;
|
||||
.app__wrap {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.sidebar__wrap {
|
||||
position: fixed;
|
||||
width: @menuWidth;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
transition: width 0.2s;
|
||||
}
|
||||
.sidebar__wrap--collapsed {
|
||||
width: @menuMinWidth;
|
||||
@{deep}(.anticon-item) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.main__wrap {
|
||||
position: absolute;
|
||||
width: calc(~"100% - @{menuWidth}");
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: @menuWidth;
|
||||
transition: all 0.2s;
|
||||
z-index: 1;
|
||||
.header__wrap {
|
||||
transition: all 0.2s;
|
||||
.navbar__wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @navbarHeight;
|
||||
padding: 0 20px 0 15px;
|
||||
position: relative;
|
||||
background: @contentBg;
|
||||
&:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border-top: 1px solid #d8dce5;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
@{deep}(.hover-container) {
|
||||
transition: background 0.2s;
|
||||
height: 100%;
|
||||
line-height: @navbarHeight + 5px;
|
||||
padding: 0 5px;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
.navbar__wrap--right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @navbarHeight;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 20px;
|
||||
@{deep}(.screenfull-container),
|
||||
@{deep}(.user-container) {
|
||||
line-height: @navbarHeight !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// content样式
|
||||
.main__wrap--content {
|
||||
height: 100%;
|
||||
@{deep}(.el-scrollbar__wrap) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
// content样式
|
||||
}
|
||||
.main__wrap--collapsed {
|
||||
width: calc(~"100% - @{menuMinWidth}");
|
||||
left: @menuMinWidth;
|
||||
}
|
||||
}
|
||||
.layout-content-has-tags {
|
||||
margin-top: @navbarHeight + @tagsViewHeight;
|
||||
}
|
||||
.has-tags {
|
||||
position: fixed;
|
||||
top: @navbarHeight;
|
||||
left: @menuWidth;
|
||||
width: calc(~"100% - @{menuWidth}");
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.ant-layout-header-collapsed,
|
||||
.has-tags-collapsed {
|
||||
left: 80px;
|
||||
width: calc(~"100% - 80px");
|
||||
|
||||
// 经典模式
|
||||
.app__wrap--Classic {
|
||||
.main__wrap--fixed--all {
|
||||
margin-top: @navbarHeight + @tagsViewHeight !important;
|
||||
height: calc(~"100% - @{navbarHeight} - @{tagsViewHeight}") !important;
|
||||
}
|
||||
.main__wrap--fixed--nav {
|
||||
margin-top: @navbarHeight !important;
|
||||
height: calc(~"100% - @{navbarHeight}") !important;
|
||||
}
|
||||
.main__wrap--fixed--tags {
|
||||
margin-top: @tagsViewHeight !important;
|
||||
height: calc(~"100% - @{tagsViewHeight}") !important;
|
||||
}
|
||||
.header__wrap--fixed {
|
||||
position: fixed !important;
|
||||
width: calc(~"100% - @{menuWidth}") !important;
|
||||
top: 0 !important;
|
||||
left: @menuWidth !important;
|
||||
z-index: 200;
|
||||
}
|
||||
.header__wrap--collapsed {
|
||||
width: calc(~"100% - @{menuMinWidth}") !important;
|
||||
left: @menuMinWidth !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,97 +1,157 @@
|
|||
<template>
|
||||
<a-layout>
|
||||
<a-layout-header :class="'ant-layout-header--' + silderTheme">
|
||||
<div :class="classObj" class="app__wrap">
|
||||
<!-- Classic -->
|
||||
<div
|
||||
class="sidebar__wrap"
|
||||
:class="{'sidebar__wrap--collapsed': collapsed}"
|
||||
>
|
||||
<logo
|
||||
v-if="show_logo"
|
||||
:theme="silderTheme"
|
||||
v-if="showLogo && layout === 'Classic'"
|
||||
:collapsed="collapsed"
|
||||
/>
|
||||
<silder
|
||||
:theme="silderTheme"
|
||||
mode="horizontal"
|
||||
class="header-silder--wrap"
|
||||
/>
|
||||
<div class="right-menu">
|
||||
<screenfull class="right-menu-item hover-effect screenfull-item" />
|
||||
<sider :layout="layout" mode="vertical" />
|
||||
</div>
|
||||
<!-- Classic -->
|
||||
|
||||
<user-info class="right-menu-item hover-effect" />
|
||||
</div>
|
||||
</a-layout-header>
|
||||
<a-layout class="layout-content">
|
||||
<a-layout-sider
|
||||
v-model:collapsed="collapsed"
|
||||
:trigger="null"
|
||||
collapsible
|
||||
:class="'ant-layout-sider--' + theme"
|
||||
class="sidebar-container-wrap"
|
||||
>
|
||||
<silder
|
||||
<!-- Top -->
|
||||
<div v-if="layout !== 'Classic'" class="sidebar__wrap--Top">
|
||||
<div>
|
||||
<logo
|
||||
v-if="showLogo"
|
||||
:collapsed="collapsed"
|
||||
:theme="theme"
|
||||
class="left-sider-wrap"
|
||||
/>
|
||||
</a-layout-sider>
|
||||
<a-layout>
|
||||
<div class="navbar-wrap">
|
||||
<hamburger :collapsed="collapsed" class="hamburger-container" @toggleClick="setCollapsed" />
|
||||
<breadcrumb class="breadcrumb-container" />
|
||||
</div>
|
||||
<div v-if="layout === 'Top'" class="sidebar__item--Top">
|
||||
<sider :layout="layout" mode="horizontal" />
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="showScreenfull || showUserInfo" class="navbar__wrap--right">
|
||||
<screenfull v-if="showScreenfull" class="hover-container screenfull-container" />
|
||||
<user-info v-if="showUserInfo" class="hover-container user-container" />
|
||||
</div>
|
||||
<a-layout-content :class="{'layout-content-has-tags':has_tags}">
|
||||
<scrollbar class="main-wrap">
|
||||
<tags-view :class="{'has-tags':has_tags, 'has-tags-collapsed': collapsed && has_tags}" />
|
||||
<app-main class="classic-module--wrap" />
|
||||
</scrollbar>
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Top -->
|
||||
|
||||
<div
|
||||
class="main__wrap"
|
||||
:class="{
|
||||
'main__wrap--collapsed': collapsed
|
||||
}"
|
||||
>
|
||||
<el-scrollbar
|
||||
class="main__wrap--content"
|
||||
:class="{
|
||||
'main__wrap--fixed--all': fixedHeader && showNavbar && showTags,
|
||||
'main__wrap--fixed--nav': fixedHeader && showNavbar && !showTags,
|
||||
'main__wrap--fixed--tags': fixedHeader && !showNavbar && showTags
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="header__wrap"
|
||||
:class="{
|
||||
'header__wrap--fixed': fixedHeader,
|
||||
'header__wrap--collapsed': fixedHeader && collapsed
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="showNavbar && layout !== 'Top'"
|
||||
class="navbar__wrap"
|
||||
>
|
||||
<hamburger
|
||||
v-if="showHamburger"
|
||||
:collapsed="collapsed"
|
||||
class="hover-container"
|
||||
@toggleClick="setCollapsed"
|
||||
/>
|
||||
<breadcrumb v-if="showBreadcrumb" />
|
||||
<!-- <div v-if="showScreenfull || showUserInfo" class="navbar__wrap--right">
|
||||
<screenfull v-if="showScreenfull" class="hover-container screenfull-container" />
|
||||
<user-info v-if="showUserInfo" class="hover-container user-container" />
|
||||
</div> -->
|
||||
</div>
|
||||
<div
|
||||
v-if="showTags"
|
||||
class="tags__wrap"
|
||||
>
|
||||
<tags-view />
|
||||
</div>
|
||||
</div>
|
||||
<app-main />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<!-- setting -->
|
||||
<setting />
|
||||
<!-- setting -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed } from 'vue'
|
||||
import Logo from '../components/Logo.vue'
|
||||
import Silder from '../components/Silder'
|
||||
import UserInfo from '../components/UserInfo.vue'
|
||||
import TagsView from '../components/TagsView.vue'
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
|
||||
import AppMain from '../components/AppMain.vue'
|
||||
import Screenfull from '_c/Screenfull/index.vue'
|
||||
import Scrollbar from '_c/Scrollbar/index.vue'
|
||||
import TagsView from '_c/TagsView/index.vue'
|
||||
import Logo from '_c/Logo/index.vue'
|
||||
import Sider from '_c/Sider/index.vue'
|
||||
import Hamburger from '_c/Hamburger/index.vue'
|
||||
import Breadcrumb from '_c/Breadcrumb/index.vue'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
import config from '_p/index/config'
|
||||
const { show_logo, has_tags, theme } = config
|
||||
import Screenfull from '_c/Screenfull/index.vue'
|
||||
import UserInfo from '_c/UserInfo/index.vue'
|
||||
|
||||
import Setting from '_c/Setting/index.vue'
|
||||
export default defineComponent({
|
||||
name: 'LeftTop',
|
||||
components: {
|
||||
Logo,
|
||||
Silder,
|
||||
UserInfo,
|
||||
TagsView,
|
||||
AppMain,
|
||||
Screenfull,
|
||||
Scrollbar,
|
||||
Sider,
|
||||
Hamburger,
|
||||
Breadcrumb
|
||||
},
|
||||
props: {
|
||||
theme: {
|
||||
type: String as PropType<'light' | 'dark'>,
|
||||
default: theme
|
||||
},
|
||||
silderTheme: {
|
||||
type: String as PropType<'light' | 'dark'>,
|
||||
default: 'light'
|
||||
}
|
||||
Breadcrumb,
|
||||
Screenfull,
|
||||
UserInfo,
|
||||
AppMain,
|
||||
TagsView,
|
||||
Logo,
|
||||
Setting
|
||||
},
|
||||
setup() {
|
||||
const layout = computed(() => appStore.layout)
|
||||
const collapsed = computed(() => appStore.collapsed)
|
||||
const showLogo = computed(() => appStore.showLogo)
|
||||
const showTags = computed(() => appStore.showTags)
|
||||
const showBreadcrumb = computed(() => appStore.showBreadcrumb)
|
||||
const showHamburger = computed(() => appStore.showHamburger)
|
||||
const showScreenfull = computed(() => appStore.showScreenfull)
|
||||
const showUserInfo = computed(() => appStore.showUserInfo)
|
||||
const showNavbar = computed(() => appStore.showNavbar)
|
||||
// const fixedNavbar = computed(() => appStore.fixedNavbar)
|
||||
// const fixedTags = computed(() => appStore.fixedTags)
|
||||
const fixedHeader = computed(() => appStore.fixedHeader)
|
||||
|
||||
const classObj = computed(() => {
|
||||
const obj = {}
|
||||
obj[`app__wrap--${layout.value}`] = true
|
||||
return obj
|
||||
})
|
||||
|
||||
function setCollapsed(collapsed: boolean): void {
|
||||
appStore.SetCollapsed(collapsed)
|
||||
}
|
||||
|
||||
return {
|
||||
classObj,
|
||||
layout,
|
||||
collapsed,
|
||||
show_logo,
|
||||
has_tags,
|
||||
showLogo,
|
||||
showTags,
|
||||
showBreadcrumb,
|
||||
showHamburger,
|
||||
showScreenfull,
|
||||
showUserInfo,
|
||||
showNavbar,
|
||||
fixedHeader,
|
||||
// fixedNavbar,
|
||||
// fixedTags,
|
||||
setCollapsed
|
||||
}
|
||||
}
|
||||
|
@ -99,87 +159,173 @@ export default defineComponent({
|
|||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@{deep}(.ant-layout-header) {
|
||||
height: @topSilderHeight;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
position: fixed;
|
||||
.app__wrap {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
&--light {
|
||||
background: @menuLightBg;
|
||||
}
|
||||
&--dark {
|
||||
background: @menuBg;
|
||||
.screenfull-item,
|
||||
.name-item {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.header-silder--wrap {
|
||||
flex: 1;
|
||||
margin: 0 50px;
|
||||
height: 100% !important;
|
||||
.ant-menu-horizontal {
|
||||
height: @topSilderHeight;
|
||||
line-height: @topSilderHeight;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.ant-menu-light {
|
||||
height: calc(~"@{topSilderHeight} - 4px");
|
||||
line-height: calc(~"@{topSilderHeight} - 2px");
|
||||
}
|
||||
}
|
||||
.right-menu {
|
||||
display: flex;
|
||||
.screenfull-item {
|
||||
line-height: @topSilderHeight;
|
||||
}
|
||||
.avatar-container {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-layout-sider--light {
|
||||
background: @menuLightBg;
|
||||
}
|
||||
.ant-layout-sider--dark {
|
||||
background: @menuBg;
|
||||
}
|
||||
.layout-content {
|
||||
margin-top: @topSilderHeight;
|
||||
height: calc(~"100vh - @{topSilderHeight}");
|
||||
.navbar-wrap {
|
||||
background: #fff;
|
||||
padding: 0 20px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
height: @navbarHeight;
|
||||
line-height: @navbarHeight;
|
||||
display: flex;
|
||||
}
|
||||
.layout-content-has-tags {
|
||||
margin-top: @tagsViewHeight;
|
||||
.mian-wrap {
|
||||
background-color: @contentBg;
|
||||
}
|
||||
}
|
||||
.left-sider-wrap {
|
||||
height: 100%;
|
||||
}
|
||||
.has-tags {
|
||||
.sidebar__wrap {
|
||||
position: fixed;
|
||||
top: @topSilderHeight + @navbarHeight;
|
||||
left: @menuWidth;
|
||||
width: calc(~"100% - @{menuWidth}");
|
||||
z-index: 20;
|
||||
transition: all 0.2s;
|
||||
width: @menuWidth;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
transition: width 0.2s;
|
||||
}
|
||||
.has-tags-collapsed {
|
||||
left: 80px;
|
||||
width: calc(~"100% - 80px");
|
||||
.sidebar__wrap--collapsed {
|
||||
width: @menuMinWidth;
|
||||
@{deep}(.anticon-item) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.main__wrap {
|
||||
position: absolute;
|
||||
width: calc(~"100% - @{menuWidth}");
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: @menuWidth;
|
||||
transition: all 0.2s;
|
||||
z-index: 1;
|
||||
.header__wrap {
|
||||
transition: all 0.2s;
|
||||
.navbar__wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @navbarHeight;
|
||||
padding: 0 20px 0 15px;
|
||||
position: relative;
|
||||
background: @contentBg;
|
||||
&:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border-top: 1px solid #d8dce5;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
@{deep}(.hover-container) {
|
||||
transition: background 0.2s;
|
||||
height: 100%;
|
||||
line-height: @navbarHeight + 5px;
|
||||
padding: 0 5px;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
.navbar__wrap--right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @navbarHeight;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 20px;
|
||||
@{deep}(.screenfull-container),
|
||||
@{deep}(.user-container) {
|
||||
line-height: @navbarHeight !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// content样式
|
||||
.main__wrap--content {
|
||||
height: 100%;
|
||||
@{deep}(.el-scrollbar__wrap) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
// content样式
|
||||
}
|
||||
.main__wrap--collapsed {
|
||||
width: calc(~"100% - @{menuMinWidth}");
|
||||
left: @menuMinWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// LeftTop模式
|
||||
.app__wrap--LeftTop {
|
||||
.main__wrap--fixed--all {
|
||||
margin-top: @navbarHeight + @tagsViewHeight !important;
|
||||
height: calc(~"100% - @{navbarHeight} - @{tagsViewHeight}") !important;
|
||||
}
|
||||
.main__wrap--fixed--nav {
|
||||
margin-top: @navbarHeight !important;
|
||||
height: calc(~"100% - @{navbarHeight}") !important;
|
||||
}
|
||||
.main__wrap--fixed--tags {
|
||||
margin-top: @tagsViewHeight !important;
|
||||
height: calc(~"100% - @{tagsViewHeight}") !important;
|
||||
}
|
||||
.header__wrap--fixed {
|
||||
position: fixed !important;
|
||||
width: calc(~"100% - @{menuWidth}") !important;
|
||||
top: @topSiderHeight !important;
|
||||
left: @menuWidth !important;
|
||||
z-index: 200;
|
||||
}
|
||||
.header__wrap--collapsed {
|
||||
width: calc(~"100% - @{menuMinWidth}") !important;
|
||||
left: @menuMinWidth !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 顶部模式
|
||||
.app__wrap--LeftTop {
|
||||
.sidebar__wrap--Top {
|
||||
height: @topSiderHeight;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
background-color: @topMenuBg;
|
||||
position: relative;
|
||||
&:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border-top: 1px solid #d8dce5;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.sidebar__item--Top {
|
||||
flex: 1;
|
||||
margin: 0 50px;
|
||||
}
|
||||
.navbar__wrap--right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @topSiderHeight;
|
||||
@{deep}(.hover-container) {
|
||||
transition: background 0.2s;
|
||||
height: 100%;
|
||||
line-height: @topSiderHeight;
|
||||
padding: 0 5px;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app__wrap--LeftTop {
|
||||
.sidebar__wrap {
|
||||
top: @topSiderHeight;
|
||||
left: 0;
|
||||
height: calc(~"100% - @{topSiderHeight}");
|
||||
}
|
||||
.main__wrap {
|
||||
width: calc(~"100% - @{menuWidth}");
|
||||
left: @menuWidth;
|
||||
height: calc(~"100% - @{topSiderHeight}");
|
||||
top: @topSiderHeight;
|
||||
}
|
||||
.main__wrap--collapsed {
|
||||
width: calc(~"100% - @{menuMinWidth}");
|
||||
left: @menuMinWidth;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,128 +1,291 @@
|
|||
<template>
|
||||
<a-layout>
|
||||
<a-layout-header :class="'ant-layout-header--' + theme">
|
||||
<logo
|
||||
v-if="show_logo"
|
||||
:theme="theme"
|
||||
/>
|
||||
<silder
|
||||
:theme="theme"
|
||||
mode="horizontal"
|
||||
class="header-silder--wrap"
|
||||
/>
|
||||
<div class="right-menu">
|
||||
<screenfull class="right-menu-item hover-effect screenfull-item" />
|
||||
|
||||
<user-info class="right-menu-item hover-effect" />
|
||||
<div :class="classObj" class="app__wrap">
|
||||
<!-- Top -->
|
||||
<div class="sidebar__wrap--Top">
|
||||
<div>
|
||||
<logo
|
||||
v-if="showLogo"
|
||||
:collapsed="collapsed"
|
||||
/>
|
||||
</div>
|
||||
</a-layout-header>
|
||||
<a-layout-content :class="{'layout-content-has-tags':has_tags}">
|
||||
<scrollbar class="main-wrap">
|
||||
<tags-view :class="{'has-tags':has_tags}" />
|
||||
<app-main class="top-module--wrap" />
|
||||
</scrollbar>
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
<div class="sidebar__item--Top">
|
||||
<sider :layout="layout" mode="horizontal" />
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="showScreenfull || showUserInfo" class="navbar__wrap--right">
|
||||
<screenfull v-if="showScreenfull" class="hover-container screenfull-container" />
|
||||
<user-info v-if="showUserInfo" class="hover-container user-container" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Top -->
|
||||
|
||||
<div
|
||||
class="main__wrap"
|
||||
:class="{
|
||||
'main__wrap--collapsed': collapsed
|
||||
}"
|
||||
>
|
||||
<el-scrollbar
|
||||
class="main__wrap--content"
|
||||
:class="{
|
||||
'main__wrap--fixed--all': fixedHeader && showNavbar && showTags,
|
||||
'main__wrap--fixed--nav': fixedHeader && showNavbar && !showTags,
|
||||
'main__wrap--fixed--tags': fixedHeader && !showNavbar && showTags
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="header__wrap"
|
||||
:class="{
|
||||
'header__wrap--fixed': fixedHeader,
|
||||
'header__wrap--collapsed': fixedHeader && collapsed
|
||||
}"
|
||||
>
|
||||
<!-- <div
|
||||
v-if="showNavbar && layout !== 'Top'"
|
||||
class="navbar__wrap"
|
||||
>
|
||||
<hamburger
|
||||
v-if="showHamburger"
|
||||
:collapsed="collapsed"
|
||||
class="hover-container"
|
||||
@toggleClick="setCollapsed"
|
||||
/>
|
||||
<breadcrumb v-if="showBreadcrumb" />
|
||||
<div v-if="showScreenfull || showUserInfo" class="navbar__wrap--right">
|
||||
<screenfull v-if="showScreenfull" class="hover-container screenfull-container" />
|
||||
<user-info v-if="showUserInfo" class="hover-container user-container" />
|
||||
</div>
|
||||
</div> -->
|
||||
<div
|
||||
v-if="showTags"
|
||||
class="tags__wrap"
|
||||
>
|
||||
<tags-view />
|
||||
</div>
|
||||
</div>
|
||||
<app-main />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<!-- setting -->
|
||||
<setting />
|
||||
<!-- setting -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import Logo from '../components/Logo.vue'
|
||||
import Silder from '../components/Silder'
|
||||
import UserInfo from '../components/UserInfo.vue'
|
||||
import TagsView from '../components/TagsView.vue'
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
|
||||
import AppMain from '../components/AppMain.vue'
|
||||
import TagsView from '_c/TagsView/index.vue'
|
||||
import Logo from '_c/Logo/index.vue'
|
||||
import Sider from '_c/Sider/index.vue'
|
||||
// import Hamburger from '_c/Hamburger/index.vue'
|
||||
// import Breadcrumb from '_c/Breadcrumb/index.vue'
|
||||
import Screenfull from '_c/Screenfull/index.vue'
|
||||
import Scrollbar from '_c/Scrollbar/index.vue'
|
||||
import config from '_p/index/config'
|
||||
const { show_logo, has_tags, theme } = config
|
||||
import UserInfo from '_c/UserInfo/index.vue'
|
||||
|
||||
import Setting from '_c/Setting/index.vue'
|
||||
export default defineComponent({
|
||||
name: 'Top',
|
||||
components: {
|
||||
Logo,
|
||||
Silder,
|
||||
UserInfo,
|
||||
TagsView,
|
||||
AppMain,
|
||||
Sider,
|
||||
// Hamburger,
|
||||
// Breadcrumb,
|
||||
Screenfull,
|
||||
Scrollbar
|
||||
},
|
||||
props: {
|
||||
theme: {
|
||||
type: String as PropType<'light' | 'dark'>,
|
||||
default: theme
|
||||
}
|
||||
UserInfo,
|
||||
AppMain,
|
||||
TagsView,
|
||||
Logo,
|
||||
Setting
|
||||
},
|
||||
setup() {
|
||||
const layout = computed(() => appStore.layout)
|
||||
const collapsed = computed(() => appStore.collapsed)
|
||||
const showLogo = computed(() => appStore.showLogo)
|
||||
const showTags = computed(() => appStore.showTags)
|
||||
const showBreadcrumb = computed(() => appStore.showBreadcrumb)
|
||||
const showHamburger = computed(() => appStore.showHamburger)
|
||||
const showScreenfull = computed(() => appStore.showScreenfull)
|
||||
const showUserInfo = computed(() => appStore.showUserInfo)
|
||||
const showNavbar = computed(() => appStore.showNavbar)
|
||||
// const fixedNavbar = computed(() => appStore.fixedNavbar)
|
||||
// const fixedTags = computed(() => appStore.fixedTags)
|
||||
const fixedHeader = computed(() => appStore.fixedHeader)
|
||||
|
||||
const classObj = computed(() => {
|
||||
const obj = {}
|
||||
obj[`app__wrap--${layout.value}`] = true
|
||||
return obj
|
||||
})
|
||||
|
||||
function setCollapsed(collapsed: boolean): void {
|
||||
appStore.SetCollapsed(collapsed)
|
||||
}
|
||||
|
||||
return {
|
||||
show_logo,
|
||||
has_tags
|
||||
classObj,
|
||||
layout,
|
||||
collapsed,
|
||||
showLogo,
|
||||
showTags,
|
||||
showBreadcrumb,
|
||||
showHamburger,
|
||||
showScreenfull,
|
||||
showUserInfo,
|
||||
showNavbar,
|
||||
fixedHeader,
|
||||
// fixedNavbar,
|
||||
// fixedTags,
|
||||
setCollapsed
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@{deep}(.ant-layout-header) {
|
||||
height: @topSilderHeight;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
position: fixed;
|
||||
.app__wrap {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
&--light {
|
||||
background: @menuLightBg;
|
||||
.sidebar__wrap {
|
||||
position: fixed;
|
||||
width: @menuWidth;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
transition: width 0.2s;
|
||||
}
|
||||
&--dark {
|
||||
background: @menuBg;
|
||||
.screenfull-item,
|
||||
.name-item {
|
||||
color: #fff;
|
||||
.sidebar__wrap--collapsed {
|
||||
width: @menuMinWidth;
|
||||
@{deep}(.anticon-item) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.header-silder--wrap {
|
||||
flex: 1;
|
||||
margin: 0 50px;
|
||||
height: 100% !important;
|
||||
.ant-menu-horizontal {
|
||||
height: @topSilderHeight;
|
||||
line-height: @topSilderHeight;
|
||||
border-bottom: 0;
|
||||
.main__wrap {
|
||||
position: absolute;
|
||||
width: calc(~"100% - @{menuWidth}");
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: @menuWidth;
|
||||
transition: all 0.2s;
|
||||
z-index: 1;
|
||||
.header__wrap {
|
||||
transition: all 0.2s;
|
||||
.navbar__wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @navbarHeight;
|
||||
padding: 0 20px 0 15px;
|
||||
position: relative;
|
||||
background: @contentBg;
|
||||
&:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border-top: 1px solid #d8dce5;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
@{deep}(.hover-container) {
|
||||
transition: background 0.2s;
|
||||
height: 100%;
|
||||
line-height: @navbarHeight + 5px;
|
||||
padding: 0 5px;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
.navbar__wrap--right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @navbarHeight;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 20px;
|
||||
@{deep}(.screenfull-container),
|
||||
@{deep}(.user-container) {
|
||||
line-height: @navbarHeight !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-menu-light {
|
||||
height: calc(~"@{topSilderHeight} - 4px");
|
||||
line-height: calc(~"@{topSilderHeight} - 4px");
|
||||
|
||||
// content样式
|
||||
.main__wrap--content {
|
||||
height: 100%;
|
||||
@{deep}(.el-scrollbar__wrap) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
// content样式
|
||||
}
|
||||
.right-menu {
|
||||
.main__wrap--collapsed {
|
||||
width: calc(~"100% - @{menuMinWidth}");
|
||||
left: @menuMinWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// 顶部模式
|
||||
.app__wrap--Top {
|
||||
.sidebar__wrap--Top {
|
||||
height: @topSiderHeight;
|
||||
display: flex;
|
||||
.screenfull-item {
|
||||
line-height: @topSilderHeight;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
background-color: @topMenuBg;
|
||||
position: relative;
|
||||
&:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
border-top: 1px solid #d8dce5;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.avatar-container {
|
||||
margin-right: 0;
|
||||
.sidebar__item--Top {
|
||||
flex: 1;
|
||||
margin: 0 50px;
|
||||
}
|
||||
.navbar__wrap--right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: @topSiderHeight;
|
||||
@{deep}(.hover-container) {
|
||||
transition: background 0.2s;
|
||||
height: 100%;
|
||||
line-height: @topSiderHeight;
|
||||
padding: 0 5px;
|
||||
text-align: center;
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-layout-content {
|
||||
margin-top: @topSilderHeight;
|
||||
height: calc(~"100vh - @{topSilderHeight}");
|
||||
.main-wrap {
|
||||
background-color: @contentBg;
|
||||
.header__wrap--fixed {
|
||||
position: fixed !important;
|
||||
width: 100% !important;
|
||||
top: @topSiderHeight !important;
|
||||
left: 0 !important;
|
||||
z-index: 200;
|
||||
}
|
||||
.main__wrap {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
height: calc(~"100% - @{topSiderHeight}");
|
||||
top: @topSiderHeight;
|
||||
}
|
||||
.main__wrap--fixed--all,
|
||||
.main__wrap--fixed--tags {
|
||||
margin-top: @navbarHeight !important;
|
||||
height: calc(~"100% - @{navbarHeight}") !important;
|
||||
}
|
||||
}
|
||||
.layout-content-has-tags {
|
||||
margin-top: @topSilderHeight + @tagsViewHeight;
|
||||
height: calc(~"100vh - @{topSilderHeight} - @{tagsViewHeight}");
|
||||
}
|
||||
.has-tags {
|
||||
position: fixed;
|
||||
top: @topSilderHeight;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 20;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -47,7 +47,7 @@ setupSvgIcon(app) // 全局注册svgIcon组件
|
|||
|
||||
setupGlobCom(app) // 注册全局公用组件
|
||||
|
||||
// setupDirectives(app) // 注册全局自定义指令
|
||||
setupDirectives(app) // 注册全局自定义指令
|
||||
|
||||
// mockXHR() // mock注册
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@ import NProgress from 'nprogress' // 引入进度条
|
|||
|
||||
import 'nprogress/nprogress.css' // 进度条样式
|
||||
|
||||
import config from './config'
|
||||
|
||||
const { user_info, title } = config
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
|
||||
import wsCache from '@/cache'
|
||||
|
||||
|
@ -20,7 +18,7 @@ import type { RouteRecordRaw } from 'vue-router'
|
|||
|
||||
const whiteList: string[] = ['/login'] // 不重定向白名单
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (wsCache.get(user_info)) {
|
||||
if (wsCache.get(appStore.userInfo)) {
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
} else {
|
||||
|
@ -55,6 +53,6 @@ router.beforeEach((to, from, next) => {
|
|||
})
|
||||
|
||||
router.afterEach((to) => {
|
||||
document.title = getPageTitle(to.meta.title, title)
|
||||
document.title = getPageTitle(to.meta.title, appStore.title)
|
||||
NProgress.done() // 结束进度条
|
||||
})
|
||||
|
|
|
@ -6,8 +6,6 @@ import { getParentLayout } from './utils'
|
|||
|
||||
/* Layout */
|
||||
const Layout = () => import('../layout/index.vue')
|
||||
/* ParentView */
|
||||
import ParentView from '_c/ParentView/index.vue'
|
||||
|
||||
/**
|
||||
* redirect: noredirect 当设置 noredirect 的时候该路由在面包屑导航中不可被点击
|
||||
|
@ -50,7 +48,8 @@ export const constantRouterMap: AppRouteRecordRaw[] = [
|
|||
name: 'NoFind',
|
||||
meta: {
|
||||
hidden: true,
|
||||
title: '404'
|
||||
title: '404',
|
||||
noTagsView: true
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -83,281 +82,305 @@ export const constantRouterMap: AppRouteRecordRaw[] = [
|
|||
}
|
||||
]
|
||||
},
|
||||
// {
|
||||
// path: '/external-link',
|
||||
// component: Layout,
|
||||
// meta: {},
|
||||
// children: [
|
||||
// {
|
||||
// path: 'http://192.168.169.57/ue/2019/doc/vue-standard/dist/',
|
||||
// meta: { title: '文档', icon: 'documentation' }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
{
|
||||
path: '/external-link',
|
||||
component: Layout,
|
||||
meta: {},
|
||||
children: [
|
||||
{
|
||||
path: 'http://192.168.169.57/ue/2019/doc/vue-standard/dist/',
|
||||
meta: { title: '文档', icon: 'documentation' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export const asyncRouterMap: AppRouteRecordRaw[] = [
|
||||
// {
|
||||
// path: '/components-demo',
|
||||
// component: Layout,
|
||||
// redirect: '/components-demo/echarts',
|
||||
// name: 'ComponentsDemo',
|
||||
// meta: {
|
||||
// title: '功能组件',
|
||||
// icon: 'component',
|
||||
// alwaysShow: true
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'echarts',
|
||||
// component: () => import('_p/index/views/components-demo/echarts/index.vue'),
|
||||
// name: 'EchartsDemo',
|
||||
// meta: {
|
||||
// title: '图表'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'image',
|
||||
// component: () => import('_p/index/views/components-demo/image/index.vue'),
|
||||
// name: 'ImageDemo',
|
||||
// meta: {
|
||||
// title: '图片'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'preview',
|
||||
// component: () => import('_p/index/views/components-demo/preview/index.vue'),
|
||||
// name: 'PreviewDemo',
|
||||
// meta: {
|
||||
// title: '图片预览'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'scroll',
|
||||
// component: () => import('_p/index/views/components-demo/scroll/index.vue'),
|
||||
// name: 'ScrollDemo',
|
||||
// meta: {
|
||||
// title: '滚动'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'count-to',
|
||||
// component: () => import('_p/index/views/components-demo/count-to/index.vue'),
|
||||
// name: 'CountToDemo',
|
||||
// meta: {
|
||||
// title: '数字动画'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'search',
|
||||
// component: () => import('_p/index/views/components-demo/search/index.vue'),
|
||||
// name: 'SearchDemo',
|
||||
// meta: {
|
||||
// title: '查询'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'button',
|
||||
// component: () => import('_p/index/views/components-demo/button/index.vue'),
|
||||
// name: 'ButtonDemo',
|
||||
// meta: {
|
||||
// title: '按钮'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'editor',
|
||||
// component: () => import('_p/index/views/components-demo/editor/index.vue'),
|
||||
// name: 'EditorDemo',
|
||||
// meta: {
|
||||
// title: '富文本编辑器'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'markdown',
|
||||
// component: () => import('_p/index/views/components-demo/markdown/index.vue'),
|
||||
// name: 'MarkdownDemo',
|
||||
// meta: {
|
||||
// title: 'markdown编辑器'
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// // {
|
||||
// // path: '/table-demo',
|
||||
// // component: Layout,
|
||||
// // redirect: '/table-demo/basic-usage',
|
||||
// // name: 'TableDemo',
|
||||
// // meta: {
|
||||
// // title: '表格',
|
||||
// // icon: 'table',
|
||||
// // alwaysShow: true
|
||||
// // },
|
||||
// // children: [
|
||||
// // // {
|
||||
// // // path: 'test',
|
||||
// // // component: () => import('_p/index/views/table-demo/test'),
|
||||
// // // name: 'test',
|
||||
// // // meta: {
|
||||
// // // title: 'test'
|
||||
// // // }
|
||||
// // // },
|
||||
// // {
|
||||
// // path: 'basic-usage',
|
||||
// // component: () => import('_p/index/views/table-demo/basic-usage/index.vue'),
|
||||
// // name: 'BasicUsage',
|
||||
// // meta: {
|
||||
// // title: '基础用法'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'table-ellipsis',
|
||||
// // component: () => import('_p/index/views/table-demo/table-ellipsis/index.vue'),
|
||||
// // name: 'TableEllipsis',
|
||||
// // meta: {
|
||||
// // title: '单元格自动省略'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'table-load',
|
||||
// // component: () => import('_p/index/views/table-demo/table-load/index.vue'),
|
||||
// // name: 'TableLoad',
|
||||
// // meta: {
|
||||
// // title: '远程加载数据'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'table-border',
|
||||
// // component: () => import('_p/index/views/table-demo/table-border/index.vue'),
|
||||
// // name: 'TableBorder',
|
||||
// // meta: {
|
||||
// // title: '带边框'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'table-merge',
|
||||
// // component: () => import('_p/index/views/table-demo/table-merge/index.vue'),
|
||||
// // name: 'TableMerge',
|
||||
// // meta: {
|
||||
// // title: '表格行/列合并'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'custom-menu',
|
||||
// // component: () => import('_p/index/views/table-demo/custom-menu/index.vue'),
|
||||
// // name: 'CustomMenu',
|
||||
// // meta: {
|
||||
// // title: '自定义筛选菜单'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'edit-cell',
|
||||
// // component: () => import('_p/index/views/table-demo/edit-cell/index.vue'),
|
||||
// // name: 'EditCell',
|
||||
// // meta: {
|
||||
// // title: '可编辑单元格'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'edit-row',
|
||||
// // component: () => import('_p/index/views/table-demo/edit-row/index.vue'),
|
||||
// // name: 'EditRow',
|
||||
// // meta: {
|
||||
// // title: '可编辑行'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'table-tree',
|
||||
// // component: () => import('_p/index/views/table-demo/table-tree/index.vue'),
|
||||
// // name: 'TableTree',
|
||||
// // meta: {
|
||||
// // title: '树形数据展示'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'table-expanded',
|
||||
// // component: () => import('_p/index/views/table-demo/table-expanded/index.vue'),
|
||||
// // name: 'TableExpanded',
|
||||
// // meta: {
|
||||
// // title: '可展开'
|
||||
// // }
|
||||
// // },
|
||||
// // {
|
||||
// // path: 'fixed-header',
|
||||
// // component: () => import('_p/index/views/table-demo/fixed-header/index.vue'),
|
||||
// // name: 'FixedHeader',
|
||||
// // meta: {
|
||||
// // title: '固定头和列'
|
||||
// // }
|
||||
// // }
|
||||
// // ]
|
||||
// // },
|
||||
// {
|
||||
// path: '/directives-demo',
|
||||
// component: Layout,
|
||||
// redirect: '/directives-demo/clipboard',
|
||||
// name: 'DirectivesDemo',
|
||||
// meta: {
|
||||
// title: '自定义指令',
|
||||
// icon: 'clipboard',
|
||||
// alwaysShow: true
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'clipboard',
|
||||
// component: () => import('_p/index/views/directives-demo/clipboard/index.vue'),
|
||||
// name: 'ClipboardDemo',
|
||||
// meta: {
|
||||
// title: 'Clipboard'
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: '/hooks-demo',
|
||||
// component: Layout,
|
||||
// redirect: '/hooks-demo/watermark',
|
||||
// name: 'HooksDemo',
|
||||
// meta: {
|
||||
// title: 'Hooks',
|
||||
// icon: 'international',
|
||||
// alwaysShow: true
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'watermark',
|
||||
// component: () => import('_p/index/views/hooks-demo/useWatermark/index.vue'),
|
||||
// name: 'UseWatermarkDemo',
|
||||
// meta: {
|
||||
// title: 'UseWaterMark'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: 'useScrollTo',
|
||||
// component: () => import('_p/index/views/hooks-demo/useScrollTo/index.vue'),
|
||||
// name: 'UseScrollToDemo',
|
||||
// meta: {
|
||||
// title: 'UseScrollTo'
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: '/icon',
|
||||
// component: Layout,
|
||||
// name: 'IconsDemo',
|
||||
// meta: {},
|
||||
// children: [
|
||||
// {
|
||||
// path: 'index',
|
||||
// component: () => import('_p/index/views/icons/index.vue'),
|
||||
// name: 'Icons',
|
||||
// meta: {
|
||||
// title: '图标',
|
||||
// icon: 'icon'
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
path: '/components-demo',
|
||||
component: Layout,
|
||||
redirect: '/components-demo/echarts',
|
||||
name: 'ComponentsDemo',
|
||||
meta: {
|
||||
title: '功能组件',
|
||||
icon: 'component',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'echarts',
|
||||
component: () => import('_p/index/views/components-demo/echarts/index.vue'),
|
||||
name: 'EchartsDemo',
|
||||
meta: {
|
||||
title: '图表'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'preview',
|
||||
component: () => import('_p/index/views/components-demo/preview/index.vue'),
|
||||
name: 'PreviewDemo',
|
||||
meta: {
|
||||
title: '图片预览'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'count-to',
|
||||
component: () => import('_p/index/views/components-demo/count-to/index.vue'),
|
||||
name: 'CountToDemo',
|
||||
meta: {
|
||||
title: '数字动画'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
component: () => import('_p/index/views/components-demo/search/index.vue'),
|
||||
name: 'SearchDemo',
|
||||
meta: {
|
||||
title: '查询'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'editor',
|
||||
component: () => import('_p/index/views/components-demo/editor/index.vue'),
|
||||
name: 'EditorDemo',
|
||||
meta: {
|
||||
title: '富文本编辑器'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'markdown',
|
||||
component: () => import('_p/index/views/components-demo/markdown/index.vue'),
|
||||
name: 'MarkdownDemo',
|
||||
meta: {
|
||||
title: 'markdown编辑器'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/table-demo',
|
||||
component: Layout,
|
||||
redirect: '/table-demo/basic-usage',
|
||||
name: 'TableDemo',
|
||||
meta: {
|
||||
title: '表格',
|
||||
icon: 'table',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
// {
|
||||
// path: 'test',
|
||||
// component: () => import('_p/index/views/table-demo/test'),
|
||||
// name: 'test',
|
||||
// meta: {
|
||||
// title: 'test'
|
||||
// }
|
||||
// },
|
||||
{
|
||||
path: 'basic-table',
|
||||
component: () => import('_p/index/views/table-demo/basic-table/index.vue'),
|
||||
name: 'BasicTable',
|
||||
meta: {
|
||||
title: '基础表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'stripe-table',
|
||||
component: () => import('_p/index/views/table-demo/stripe-table/index.vue'),
|
||||
name: 'StripeTable',
|
||||
meta: {
|
||||
title: '带斑马纹表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'border-table',
|
||||
component: () => import('_p/index/views/table-demo/border-table/index.vue'),
|
||||
name: 'BorderTable',
|
||||
meta: {
|
||||
title: '带边框表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'state-table',
|
||||
component: () => import('_p/index/views/table-demo/state-table/index.vue'),
|
||||
name: 'StateTable',
|
||||
meta: {
|
||||
title: '带状态表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-header',
|
||||
component: () => import('_p/index/views/table-demo/fixed-header/index.vue'),
|
||||
name: 'FixedHeader',
|
||||
meta: {
|
||||
title: '固定表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-column',
|
||||
component: () => import('_p/index/views/table-demo/fixed-column/index.vue'),
|
||||
name: 'FixedColumn',
|
||||
meta: {
|
||||
title: '固定列'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-column-header',
|
||||
component: () => import('_p/index/views/table-demo/fixed-column-header/index.vue'),
|
||||
name: 'FixedColumnHeader',
|
||||
meta: {
|
||||
title: '固定列和表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fluid-height',
|
||||
component: () => import('_p/index/views/table-demo/fluid-height/index.vue'),
|
||||
name: 'FluidHeight',
|
||||
meta: {
|
||||
title: '流体高度'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'multi-header',
|
||||
component: () => import('_p/index/views/table-demo/multi-header/index.vue'),
|
||||
name: 'MultiHeader',
|
||||
meta: {
|
||||
title: '多级表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'single-choice',
|
||||
component: () => import('_p/index/views/table-demo/single-choice/index.vue'),
|
||||
name: 'SingleChoice',
|
||||
meta: {
|
||||
title: '单选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'multiple-choice',
|
||||
component: () => import('_p/index/views/table-demo/multiple-choice/index.vue'),
|
||||
name: 'MultipleChoice',
|
||||
meta: {
|
||||
title: '多选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'sort-table',
|
||||
component: () => import('_p/index/views/table-demo/sort-table/index.vue'),
|
||||
name: 'SortTable',
|
||||
meta: {
|
||||
title: '排序'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'screen-table',
|
||||
component: () => import('_p/index/views/table-demo/screen-table/index.vue'),
|
||||
name: 'ScreenTable',
|
||||
meta: {
|
||||
title: '筛选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'expand-row',
|
||||
component: () => import('_p/index/views/table-demo/expand-row/index.vue'),
|
||||
name: 'ExpandRow',
|
||||
meta: {
|
||||
title: '展开行'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tree-and-load',
|
||||
component: () => import('_p/index/views/table-demo/tree-and-load/index.vue'),
|
||||
name: 'TreeAndLoad',
|
||||
meta: {
|
||||
title: '树形数据与懒加载'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'custom-header',
|
||||
component: () => import('_p/index/views/table-demo/custom-header/index.vue'),
|
||||
name: 'CustomHeader',
|
||||
meta: {
|
||||
title: '自定义表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'total-table',
|
||||
component: () => import('_p/index/views/table-demo/total-table/index.vue'),
|
||||
name: 'TotalTable',
|
||||
meta: {
|
||||
title: '表尾合计行'
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/directives-demo',
|
||||
component: Layout,
|
||||
redirect: '/directives-demo/clipboard',
|
||||
name: 'DirectivesDemo',
|
||||
meta: {
|
||||
title: '自定义指令',
|
||||
icon: 'clipboard',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'clipboard',
|
||||
component: () => import('_p/index/views/directives-demo/clipboard/index.vue'),
|
||||
name: 'ClipboardDemo',
|
||||
meta: {
|
||||
title: 'Clipboard'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/hooks-demo',
|
||||
component: Layout,
|
||||
redirect: '/hooks-demo/watermark',
|
||||
name: 'HooksDemo',
|
||||
meta: {
|
||||
title: 'Hooks',
|
||||
icon: 'international',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'watermark',
|
||||
component: () => import('_p/index/views/hooks-demo/useWatermark/index.vue'),
|
||||
name: 'UseWatermarkDemo',
|
||||
meta: {
|
||||
title: 'UseWaterMark'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'useScrollTo',
|
||||
component: () => import('_p/index/views/hooks-demo/useScrollTo/index.vue'),
|
||||
name: 'UseScrollToDemo',
|
||||
meta: {
|
||||
title: 'UseScrollTo'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/icon',
|
||||
component: Layout,
|
||||
name: 'IconsDemo',
|
||||
meta: {},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('_p/index/views/icons/index.vue'),
|
||||
name: 'Icons',
|
||||
meta: {
|
||||
title: '图标',
|
||||
icon: 'icon'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level',
|
||||
component: Layout,
|
||||
|
|
|
@ -15,6 +15,8 @@ export interface AppState {
|
|||
showScreenfull: Boolean
|
||||
showUserInfo: Boolean
|
||||
title: String
|
||||
logoTitle: String
|
||||
userInfo: String
|
||||
}
|
||||
|
||||
@Module({ dynamic: true, namespaced: true, store, name: 'app' })
|
||||
|
@ -31,7 +33,9 @@ class App extends VuexModule implements AppState {
|
|||
public showHamburger = true // 是否显示侧边栏缩收按钮
|
||||
public showScreenfull = true // 是否全屏按钮
|
||||
public showUserInfo = true // 是否显示用户头像
|
||||
public title = 'vue-antdv-admin' // 标题
|
||||
public title = 'vue-element-plus-admin' // 标题
|
||||
public logoTitle = 'vue-ElPlus-admin' // logo标题
|
||||
public userInfo = 'userInfo' // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突
|
||||
|
||||
@Mutation
|
||||
private SET_COLLAPSED(collapsed: boolean): void {
|
||||
|
@ -85,6 +89,10 @@ class App extends VuexModule implements AppState {
|
|||
private SET_TITLE(title: string): void {
|
||||
this.title = title
|
||||
}
|
||||
@Mutation
|
||||
private SET_LOGOTITLE(logoTitle: string): void {
|
||||
this.logoTitle = logoTitle
|
||||
}
|
||||
|
||||
@Action
|
||||
public SetCollapsed(collapsed: boolean): void {
|
||||
|
@ -138,6 +146,10 @@ class App extends VuexModule implements AppState {
|
|||
public SetTitle(title: string): void {
|
||||
this.SET_TITLE(title)
|
||||
}
|
||||
@Action
|
||||
public SetLogoTitle(logoTitle: string): void {
|
||||
this.SET_LOGOTITLE(logoTitle)
|
||||
}
|
||||
}
|
||||
|
||||
export const appStore = getModule<App>(App)
|
||||
|
|
|
@ -1,47 +1,47 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="扩展 antdv 的 Button 组件,实现 primary、success、warning、info、default 等主题样式。" style="margin-bottom: 20px;" />
|
||||
<el-alert message="扩展 antdv 的 Button 组件,实现 primary、success、warning、info、default 等主题样式。" style="margin-bottom: 20px;" />
|
||||
<div class="btn__wrap">default</div>
|
||||
<div class="btn__item">
|
||||
<a-button>默认按钮</a-button>
|
||||
<a-button shape="round">默认按钮</a-button>
|
||||
<a-button loading>默认按钮</a-button>
|
||||
<a-button disabled>默认按钮</a-button>
|
||||
<el-button>默认按钮</el-button>
|
||||
<el-button shape="round">默认按钮</el-button>
|
||||
<el-button loading>默认按钮</el-button>
|
||||
<el-button disabled>默认按钮</el-button>
|
||||
</div>
|
||||
<div class="btn__wrap">primary</div>
|
||||
<div class="btn__item">
|
||||
<a-button type="primary">主要按钮</a-button>
|
||||
<a-button type="primary" shape="round">主要按钮</a-button>
|
||||
<a-button type="primary" loading>主要按钮</a-button>
|
||||
<a-button type="primary" disabled>主要按钮</a-button>
|
||||
<el-button type="primary">主要按钮</el-button>
|
||||
<el-button type="primary" shape="round">主要按钮</el-button>
|
||||
<el-button type="primary" loading>主要按钮</el-button>
|
||||
<el-button type="primary" disabled>主要按钮</el-button>
|
||||
</div>
|
||||
<div class="btn__wrap">success</div>
|
||||
<div class="btn__item">
|
||||
<a-button type="success">成功按钮</a-button>
|
||||
<a-button type="success" shape="round">成功按钮</a-button>
|
||||
<a-button type="success" loading>成功按钮</a-button>
|
||||
<a-button type="success" disabled>成功按钮</a-button>
|
||||
<el-button type="success">成功按钮</el-button>
|
||||
<el-button type="success" shape="round">成功按钮</el-button>
|
||||
<el-button type="success" loading>成功按钮</el-button>
|
||||
<el-button type="success" disabled>成功按钮</el-button>
|
||||
</div>
|
||||
<div class="btn__wrap">warning</div>
|
||||
<div class="btn__item">
|
||||
<a-button type="warning">警告按钮</a-button>
|
||||
<a-button type="warning" shape="round">警告按钮</a-button>
|
||||
<a-button type="warning" loading>警告按钮</a-button>
|
||||
<a-button type="warning" disabled>警告按钮</a-button>
|
||||
<el-button type="warning">警告按钮</el-button>
|
||||
<el-button type="warning" shape="round">警告按钮</el-button>
|
||||
<el-button type="warning" loading>警告按钮</el-button>
|
||||
<el-button type="warning" disabled>警告按钮</el-button>
|
||||
</div>
|
||||
<div class="btn__wrap">danger</div>
|
||||
<div class="btn__item">
|
||||
<a-button type="danger">危险按钮</a-button>
|
||||
<a-button type="danger" shape="round">危险按钮</a-button>
|
||||
<a-button type="danger" loading>危险按钮</a-button>
|
||||
<a-button type="danger" disabled>危险按钮</a-button>
|
||||
<el-button type="danger">危险按钮</el-button>
|
||||
<el-button type="danger" shape="round">危险按钮</el-button>
|
||||
<el-button type="danger" loading>危险按钮</el-button>
|
||||
<el-button type="danger" disabled>危险按钮</el-button>
|
||||
</div>
|
||||
<div class="btn__wrap">info</div>
|
||||
<div class="btn__item">
|
||||
<a-button type="info">信息按钮</a-button>
|
||||
<a-button type="info" shape="round">信息按钮</a-button>
|
||||
<a-button type="info" loading>信息按钮</a-button>
|
||||
<a-button type="info" disabled>信息按钮</a-button>
|
||||
<el-button type="info">信息按钮</el-button>
|
||||
<el-button type="info" shape="round">信息按钮</el-button>
|
||||
<el-button type="info" loading>信息按钮</el-button>
|
||||
<el-button type="info" disabled>信息按钮</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="基于 vue-count-to 进行改造,支持所有 vue-count-to 参数。" style="margin-bottom: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 vue-count-to 进行改造,支持所有 vue-count-to 参数。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
|
||||
<div class="count-to">
|
||||
<count-to
|
||||
|
@ -17,44 +23,44 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="action">
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="8">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>startVal:</span><a-input-number v-model:value="startVal" :min="0" />
|
||||
<span>startVal:</span><el-input-number v-model="startVal" :min="0" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>endVal:</span><a-input-number v-model:value="endVal" :min="1" />
|
||||
<span>endVal:</span><el-input-number v-model="endVal" :min="1" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>duration:</span><a-input-number v-model:value="duration" :min="1000" />
|
||||
<span>duration:</span><el-input-number v-model="duration" :min="1000" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>separator:</span><a-input v-model:value="separator" />
|
||||
<span>separator:</span><el-input v-model="separator" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>prefix:</span><a-input v-model:value="prefix" />
|
||||
<span>prefix:</span><el-input v-model="prefix" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>suffix:</span><a-input v-model:value="suffix" />
|
||||
<span>suffix:</span><el-input v-model="suffix" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<div style="text-align: center;margin-top: 20px;">
|
||||
<a-button type="primary" @click="start">start</a-button>
|
||||
<a-button style="margin-left: 10px;" @click="pauseResume">pause/resume</a-button>
|
||||
<el-button type="primary" @click="start">start</el-button>
|
||||
<el-button style="margin-left: 10px;" @click="pauseResume">pause/resume</el-button>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -121,10 +127,11 @@ export default defineComponent({
|
|||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
&>span {
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
@{deep}(.ant-input-number) {
|
||||
@{deep}(.el-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,35 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="统一封装 Echart 组件,自适应宽度,只需传入 options 与 height 属性即可展示对应的图表。" type="info" style="margin-bottom: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="统一封装 Echart 组件,自适应宽度,只需传入 options 与 height 属性即可展示对应的图表。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
|
||||
<a-row :gutter="20">
|
||||
<a-col :span="10">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10">
|
||||
<div class="chart-wrap">
|
||||
<echart :options="pieOptions" :height="'300px'" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="14">
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="chart-wrap">
|
||||
<echart :options="barOptions" :height="'300px'" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="14">
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="chart-wrap">
|
||||
<echart :options="lineOptions" :height="'300px'" />
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="10">
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<div class="chart-wrap">
|
||||
<echart :options="pieOptions2" :height="'300px'" />
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="基于 wangeditor 封装的 富文本 组件。" style="margin-bottom: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 wangeditor 封装的 富文本 组件。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<editor v-model:value="html" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="抽取于 Element 的 Image 组件进行改造,在保留原生img的特性下,支持懒加载,自定义占位、加载失败等。" style="margin-bottom: 20px;" />
|
||||
<div class="demo-image">
|
||||
<div v-for="fit in fits" :key="fit" class="block">
|
||||
<span class="demonstration">{{ fit }}</span>
|
||||
<c-image
|
||||
:src="url"
|
||||
:fit="fit"
|
||||
style="width: 100px; height: 100px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-alert message="占位内容" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<div class="demo-image__placeholder">
|
||||
<div class="block">
|
||||
<span class="demonstration">默认</span>
|
||||
<c-image :src="src" style="width: 300px; height: 200px" />
|
||||
</div>
|
||||
<div class="block">
|
||||
<span class="demonstration">自定义</span>
|
||||
<c-image :src="src" style="width: 300px; height: 200px">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">
|
||||
加载中...
|
||||
</div>
|
||||
</template>
|
||||
</c-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-alert message="加载失败" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<div class="demo-image__error">
|
||||
<div class="block">
|
||||
<span class="demonstration">默认</span>
|
||||
<c-image style="width: 300px; height: 200px" />
|
||||
</div>
|
||||
<div class="block">
|
||||
<span class="demonstration">自定义</span>
|
||||
<c-image style="width: 300px; height: 200px">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<PictureOutlined />
|
||||
</div>
|
||||
</template>
|
||||
</c-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-alert message="懒加载" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<div class="demo-image__lazy">
|
||||
<c-image
|
||||
v-for="url in urls"
|
||||
:key="url"
|
||||
:src="url"
|
||||
lazy
|
||||
style="display: block;min-height: 200px;margin-bottom: 10px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import CImage from '_c/Image/index.vue'
|
||||
import { PictureOutlined } from '@ant-design/icons-vue'
|
||||
export default defineComponent({
|
||||
// name: 'Image',
|
||||
components: {
|
||||
CImage,
|
||||
PictureOutlined
|
||||
},
|
||||
setup() {
|
||||
const fits = ref<string[]>(['fill', 'contain', 'cover', 'none', 'scale-down'])
|
||||
const url = ref<string>('https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg')
|
||||
const src = ref<string>('https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg')
|
||||
const urls = ref<string[]>([
|
||||
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
|
||||
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
|
||||
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
|
||||
'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg',
|
||||
'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg',
|
||||
'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg',
|
||||
'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg'
|
||||
])
|
||||
return {
|
||||
fits,
|
||||
url,
|
||||
src,
|
||||
urls
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.demo-image,
|
||||
.demo-image__placeholder,
|
||||
.demo-image__error {
|
||||
background: #fff;
|
||||
}
|
||||
.demo-image .block,
|
||||
.demo-image__error .block,
|
||||
.demo-image__placeholder .block {
|
||||
padding: 30px 0;
|
||||
text-align: center;
|
||||
border-right: 1px solid #eff2f6;
|
||||
display: inline-block;
|
||||
width: 20%;
|
||||
box-sizing: border-box;
|
||||
vertical-align: top;
|
||||
.demonstration {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.image__error,
|
||||
.image-slot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #c0c4cc;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
}
|
||||
.demo-image__error .block,
|
||||
.demo-image__placeholder .block {
|
||||
text-align: center;
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.demo-image__lazy {
|
||||
width: 800px;
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="基于 vditor 封装的 Markdown编辑器 组件。" style="margin-bottom: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 vditor 封装的 Markdown编辑器 组件。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<markdown v-model:value="html" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,8 +1,20 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="抽取于 Element 的图片预览组件进行改造,实现函数式调用组件,无需基于图片进行点击预览。" style="margin-bottom: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="抽取于 Element 的图片预览组件进行改造,实现函数式调用组件,无需基于图片进行点击预览。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
|
||||
<a-alert message="有底图预览" style="margin-bottom: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="有底图预览。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<div class="img-wrap">
|
||||
<div
|
||||
v-for="(item, $index) in imgList"
|
||||
|
@ -14,18 +26,30 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a-alert message="无底图预览" style="margin-bottom: 20px; margin-top: 20px;" />
|
||||
<a-button type="primary" @click="showNoImg">点击预览</a-button>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="无底图预览。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px; margin-top: 20px;"
|
||||
/>
|
||||
<el-button type="primary" @click="showNoImg">点击预览</el-button>
|
||||
|
||||
<a-alert message="点击事件,包含图片点击事件以及关闭事件。" style="margin-bottom: 20px; margin-top: 20px;" />
|
||||
<a-button type="primary" @click="showImg">点击预览</a-button>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="点击事件,包含图片点击事件以及关闭事件。。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px; margin-top: 20px;"
|
||||
/>
|
||||
<el-button type="primary" @click="showImg">点击预览</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import { createImgPreview } from '_c/Preview/functional'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
export default defineComponent({
|
||||
// name: 'PreviewDemo',
|
||||
setup() {
|
||||
|
@ -59,10 +83,10 @@ export default defineComponent({
|
|||
show: true,
|
||||
index: 0,
|
||||
onSelect: (i: number) => {
|
||||
message.info('当前点击的图片索引:' + i)
|
||||
ElMessage.info('当前点击的图片索引:' + i)
|
||||
},
|
||||
onClose: (i: number) => {
|
||||
message.info('关闭之后的图片索引:' + i)
|
||||
ElMessage.info('关闭之后的图片索引:' + i)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="抽取于 Element 的 Scrollbar 组件进行改造统一美化各个浏览器滚动条,保持一致性。" style="margin-bottom: 20px;" />
|
||||
<el-alert message="抽取于 Element 的 Scrollbar 组件进行改造统一美化各个浏览器滚动条,保持一致性。" style="margin-bottom: 20px;" />
|
||||
|
||||
<a-alert message="横向滚动,外围容器需要设置固定宽度。" style="margin-bottom: 20px;" />
|
||||
<el-alert message="横向滚动,外围容器需要设置固定宽度。" style="margin-bottom: 20px;" />
|
||||
<div class="deom__wrap deom__wrap--horizontal">
|
||||
<scrollbar>
|
||||
<ul class="deom-ul__wrap">
|
||||
|
@ -11,7 +11,7 @@
|
|||
</scrollbar>
|
||||
</div>
|
||||
|
||||
<a-alert message="纵向滚动,外围容器需要设置固定高度。" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<el-alert message="纵向滚动,外围容器需要设置固定高度。" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<div class="deom__wrap deom__wrap--vertical">
|
||||
<scrollbar>
|
||||
<ul class="deom-ul__wrap">
|
||||
|
|
|
@ -2,16 +2,16 @@ export const classicData = [
|
|||
{
|
||||
label: '即时配送',
|
||||
value: true,
|
||||
type: 'switch',
|
||||
itemType: 'switch',
|
||||
field: 'delivery'
|
||||
},
|
||||
{
|
||||
label: '活动名称',
|
||||
value: '',
|
||||
type: 'input',
|
||||
placeholder: '活动名称',
|
||||
itemType: 'input',
|
||||
field: 'name',
|
||||
allowClear: true,
|
||||
placeholder: '活动名称',
|
||||
clearable: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
|
@ -21,11 +21,11 @@ export const classicData = [
|
|||
},
|
||||
{
|
||||
label: '活动区域',
|
||||
value: undefined,
|
||||
type: 'select',
|
||||
value: '',
|
||||
itemType: 'select',
|
||||
placeholder: '活动区域',
|
||||
clearable: true,
|
||||
field: 'region',
|
||||
allowClear: true,
|
||||
options: [
|
||||
{
|
||||
title: '区域一',
|
||||
|
@ -38,7 +38,7 @@ export const classicData = [
|
|||
],
|
||||
rules: [
|
||||
{
|
||||
type: 'string',
|
||||
itemType: 'string',
|
||||
required: true,
|
||||
message: '请选择活动区域'
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export const classicData = [
|
|||
{
|
||||
label: '特殊资源',
|
||||
value: '2',
|
||||
type: 'radio',
|
||||
itemType: 'radio',
|
||||
field: 'resource',
|
||||
radioType: 'button', // button or radio
|
||||
options: [
|
||||
|
@ -61,87 +61,89 @@ export const classicData = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '组织机构',
|
||||
value: [],
|
||||
type: 'treeSelect',
|
||||
field: 'company',
|
||||
allowClear: true,
|
||||
placeholder: '请选择组织机构',
|
||||
treeCheckable: false,
|
||||
maxTagCount: 2,
|
||||
options: [
|
||||
{
|
||||
title: 'Node1',
|
||||
value: '0-0',
|
||||
key: '0-0',
|
||||
children: [
|
||||
{
|
||||
title: 'Child Node1',
|
||||
value: '0-0-0',
|
||||
key: '0-0-0'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Node2',
|
||||
value: '0-1',
|
||||
key: '0-1',
|
||||
children: [
|
||||
{
|
||||
title: 'Child Node3',
|
||||
value: '0-1-0',
|
||||
key: '0-1-0',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
title: 'Child Node4',
|
||||
value: '0-1-1',
|
||||
key: '0-1-1'
|
||||
},
|
||||
{
|
||||
title: 'Child Node5',
|
||||
value: '0-1-2',
|
||||
key: '0-1-2'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
// {
|
||||
// label: '组织机构',
|
||||
// value: [],
|
||||
// itemType: 'treeSelect',
|
||||
// field: 'company',
|
||||
// allowClear: true,
|
||||
// placeholder: '请选择组织机构',
|
||||
// treeCheckable: false,
|
||||
// maxTagCount: 2,
|
||||
// options: [
|
||||
// {
|
||||
// title: 'Node1',
|
||||
// value: '0-0',
|
||||
// key: '0-0',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Child Node1',
|
||||
// value: '0-0-0',
|
||||
// key: '0-0-0'
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// title: 'Node2',
|
||||
// value: '0-1',
|
||||
// key: '0-1',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Child Node3',
|
||||
// value: '0-1-0',
|
||||
// key: '0-1-0',
|
||||
// disabled: true
|
||||
// },
|
||||
// {
|
||||
// title: 'Child Node4',
|
||||
// value: '0-1-1',
|
||||
// key: '0-1-1'
|
||||
// },
|
||||
// {
|
||||
// title: 'Child Node5',
|
||||
// value: '0-1-2',
|
||||
// key: '0-1-2'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
label: '日选择器',
|
||||
value: null,
|
||||
type: 'datePicker',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date1',
|
||||
allowClear: true,
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
clearable: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择日期'
|
||||
},
|
||||
{
|
||||
label: '月选择器',
|
||||
value: null,
|
||||
type: 'monthPicker',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date2',
|
||||
valueFormat: 'YYYY-MM',
|
||||
allowClear: true,
|
||||
clearable: true,
|
||||
format: 'YYYY-MM',
|
||||
placeholder: '请选择日期'
|
||||
},
|
||||
{
|
||||
label: '范围选择器',
|
||||
value: null,
|
||||
type: 'rangePicker',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
value: [],
|
||||
itemType: 'datePicker',
|
||||
field: 'date3',
|
||||
allowClear: true,
|
||||
placeholder: ['请选择日期', '请选择日期']
|
||||
clearable: true,
|
||||
type: 'daterange',
|
||||
rangeSeparator: '至',
|
||||
startPlaceholder: '开始日期',
|
||||
endPlaceholder: '结束日期'
|
||||
},
|
||||
{
|
||||
label: '周选择器',
|
||||
value: null,
|
||||
type: 'weekPicker',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date4',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
allowClear: true,
|
||||
type: 'week',
|
||||
clearable: true,
|
||||
placeholder: '请选择日期'
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="封装 antdv 的 Form 组件,实现查询、重置等功能,并提供了三种布局风格。" style="margin-bottom: 20px;" />
|
||||
<a-alert message="经典风格" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="封装 Element 的 Form 组件,实现查询、重置等功能,并提供了三种布局风格。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="经典风格。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;margin-top: 20px;"
|
||||
/>
|
||||
<div class="searh">
|
||||
<search
|
||||
:data="classicData"
|
||||
|
@ -13,7 +25,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a-alert message="底部操作按钮风格" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="底部操作按钮风格。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;margin-top: 20px;"
|
||||
/>
|
||||
<div class="searh">
|
||||
<search
|
||||
layout="bottom"
|
||||
|
@ -26,7 +44,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a-alert message="右侧操作按钮风格" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="右侧操作按钮风格。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;margin-top: 20px;"
|
||||
/>
|
||||
<div class="searh">
|
||||
<search
|
||||
layout="right"
|
||||
|
|
|
@ -16,70 +16,6 @@
|
|||
<div class="chart__wrap">
|
||||
<echart :options="lineEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
<panel-group />
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10">
|
||||
<div class="chart__wrap">
|
||||
<echart :options="pieEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="chart__wrap">
|
||||
<echart :options="barEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="chart__wrap">
|
||||
<echart :options="lineEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
<panel-group />
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10">
|
||||
<div class="chart__wrap">
|
||||
<echart :options="pieEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="chart__wrap">
|
||||
<echart :options="barEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="chart__wrap">
|
||||
<echart :options="lineEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
<panel-group />
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10">
|
||||
<div class="chart__wrap">
|
||||
<echart :options="pieEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="chart__wrap">
|
||||
<echart :options="barEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="chart__wrap">
|
||||
<echart :options="lineEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
<panel-group />
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10">
|
||||
<div class="chart__wrap">
|
||||
<echart :options="pieEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="chart__wrap">
|
||||
<echart :options="barEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="chart__wrap">
|
||||
<echart :options="lineEchatOptions" :height="'300px'" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,38 +1,56 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="自定义指令:v-clipboard,用于复制文本。" style="margin-bottom: 20px;" />
|
||||
<a-alert message="基础示例" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="自定义指令:v-clipboard,用于复制文本。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基础示例。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;margin-top: 20px;"
|
||||
/>
|
||||
<div class="input__wrap">
|
||||
<a-input v-model:value="inputVal1" placeholder="请输入要复制的文本" />
|
||||
<a-button v-clipboard="inputVal1" type="primary">复制</a-button>
|
||||
<el-input v-model="inputVal1" placeholder="请输入要复制的文本" />
|
||||
<el-button v-clipboard="inputVal1" type="primary">复制</el-button>
|
||||
</div>
|
||||
|
||||
<a-alert message="自定义回调方法" style="margin-bottom: 20px;margin-top: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="自定义回调方法。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;margin-top: 20px;"
|
||||
/>
|
||||
<div class="input__wrap">
|
||||
<a-input v-model:value="inputVal2" placeholder="请输入要复制的文本" />
|
||||
<a-button
|
||||
<el-input v-model="inputVal2" placeholder="请输入要复制的文本" />
|
||||
<el-button
|
||||
v-clipboard="inputVal2"
|
||||
v-clipboard:success="clipboardSuccess"
|
||||
v-clipboard:error="clipboardSuccess"
|
||||
type="primary"
|
||||
>复制</a-button>
|
||||
>复制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
export default defineComponent({
|
||||
// name: 'Clipboard'
|
||||
setup() {
|
||||
const inputVal1 = ref<string>('')
|
||||
const inputVal2 = ref<string>('')
|
||||
function clipboardSuccess(val: any) {
|
||||
message.success('我是自定义成功回调:' + val.text)
|
||||
ElMessage.success('我是自定义成功回调:' + val.text)
|
||||
}
|
||||
function clipboardError() {
|
||||
message.error('我是自定义失败回调')
|
||||
ElMessage.error('我是自定义失败回调')
|
||||
}
|
||||
return {
|
||||
inputVal1, inputVal2,
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="useScrollTo,提供JS滚动过渡动画功能。" style="margin-bottom: 20px;" />
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="useScrollTo,提供JS滚动过渡动画功能。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<div class="button__wrap">
|
||||
<a-button type="primary" @click="scrollTo(100, 'scrollTop')">垂直滚动100px</a-button>
|
||||
<a-button type="primary" @click="scrollTo(800, 'scrollTop')">垂直滚动800px</a-button>
|
||||
<a-button type="primary" @click="scrollTo(100, 'scrollLeft')">水平滚动100px</a-button>
|
||||
<a-button type="primary" @click="scrollTo(500, 'scrollLeft')">水平滚动500px</a-button>
|
||||
<el-button type="primary" @click="scrollTo(100, 'scrollTop')">垂直滚动100px</el-button>
|
||||
<el-button type="primary" @click="scrollTo(800, 'scrollTop')">垂直滚动800px</el-button>
|
||||
<el-button type="primary" @click="scrollTo(100, 'scrollLeft')">水平滚动100px</el-button>
|
||||
<el-button type="primary" @click="scrollTo(500, 'scrollLeft')">水平滚动500px</el-button>
|
||||
</div>
|
||||
<div class="deom__wrap">
|
||||
<scrollbar ref="scrollContainer">
|
||||
<el-scrollbar ref="scrollContainer">
|
||||
<ul class="deom-ul__wrap">
|
||||
<li v-for="i in 100" :key="i">{{ i }}</li>
|
||||
</ul>
|
||||
</scrollbar>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -20,17 +26,13 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, ref, unref } from 'vue'
|
||||
import { useScrollTo } from '@/hooks/useScrollTo'
|
||||
import Scrollbar from '_c/Scrollbar/index.vue'
|
||||
export default defineComponent({
|
||||
// name: 'UseScrollToDemo',
|
||||
components: {
|
||||
Scrollbar
|
||||
},
|
||||
setup() {
|
||||
const scrollContainer = ref<HTMLElement | null>(null)
|
||||
|
||||
function scrollTo(to: number, position: string): void {
|
||||
const $scrollWrapper: any = (unref(scrollContainer) as any).$.wrap
|
||||
const $scrollWrapper: any = (unref(scrollContainer) as any).wrap
|
||||
const { start } = useScrollTo({
|
||||
el: $scrollWrapper,
|
||||
position: position,
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert message="useWatermark,为整个系统提供水印功能。" style="margin-bottom: 20px;" />
|
||||
<a-button type="primary" @click="setWatermark('vue-antdv-admin')">创建水印</a-button>
|
||||
<a-button type="danger" @click="clear">清除水印</a-button>
|
||||
<a-button type="warning" @click="setWatermark('vue-antdv-admin-new')">重置水印</a-button>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="useWatermark,为整个系统提供水印功能。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<el-button type="primary" @click="setWatermark('vue-antdv-admin')">创建水印</el-button>
|
||||
<el-button type="danger" @click="clear">清除水印</el-button>
|
||||
<el-button type="warning" @click="setWatermark('vue-antdv-admin-new')">重置水印</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -5,15 +5,12 @@
|
|||
:key="item"
|
||||
v-clipboard="generateIconCode(item)"
|
||||
>
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
{{ generateIconCode(item) }}
|
||||
</template>
|
||||
<el-tooltip placement="top" :content="generateIconCode(item)">
|
||||
<div class="icon-item">
|
||||
<svg-icon :icon-class="item" class-name="disabled" />
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -61,7 +61,7 @@ import { defineComponent, ref, unref, reactive, watch } from 'vue'
|
|||
import { useRouter } from 'vue-router'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { permissionStore } from '_p/index/store/modules/permission'
|
||||
import config from '_p/index/config'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
import wsCache from '@/cache'
|
||||
|
||||
interface FormModule {
|
||||
|
@ -106,7 +106,7 @@ export default defineComponent({
|
|||
permissionStore.addRouters.forEach(async(route: RouteRecordRaw) => {
|
||||
await addRoute(route.name!, route) // 动态添加可访问路由表
|
||||
})
|
||||
wsCache.set(config.user_info, form)
|
||||
wsCache.set(appStore.userInfo, form)
|
||||
permissionStore.SetIsAddRouters(true)
|
||||
push({ path: redirect.value || '/' })
|
||||
})
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 基础表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'BasicTable',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,118 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data-source="data"
|
||||
>
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
</template>
|
||||
<template #customTitle>
|
||||
<span><smile-outlined /> Name</span>
|
||||
</template>
|
||||
<template #tags="{ text: tags }">
|
||||
<span>
|
||||
<a-tag
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
:color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
|
||||
>
|
||||
{{ tag.toUpperCase() }}
|
||||
</a-tag>
|
||||
</span>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<span>
|
||||
<a>Invite -- {{ record.name }}</a>
|
||||
<a-divider type="vertical" />
|
||||
<a>Delete</a>
|
||||
<a-divider type="vertical" />
|
||||
<a class="ant-dropdown-link"> More actions <down-outlined /> </a>
|
||||
</span>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
import { SmileOutlined, DownOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: { title: 'customTitle', customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age'
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address'
|
||||
},
|
||||
{
|
||||
title: 'Tags',
|
||||
key: 'tags',
|
||||
dataIndex: 'tags',
|
||||
slots: { customRender: 'tags' }
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'action',
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
tags: ['nice', 'developer']
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
tags: ['loser']
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher']
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'BasicUsage',
|
||||
components: {
|
||||
ComTable,
|
||||
SmileOutlined,
|
||||
DownOutlined
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
return {
|
||||
loading,
|
||||
columns,
|
||||
data
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带边框表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
border
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'BorderTable',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,111 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 自定义表头"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData.filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))"
|
||||
>
|
||||
<template #actionHeader>
|
||||
<el-input
|
||||
v-model="search"
|
||||
size="mini"
|
||||
placeholder="输入关键字搜索"
|
||||
/>
|
||||
</template>
|
||||
<template #action="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
@click="handleEdit(scope.$index, scope.row)"
|
||||
>Edit</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="danger"
|
||||
@click="handleDelete(scope.$index, scope.row)"
|
||||
>Delete</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
slots: {
|
||||
header: 'actionHeader',
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎1',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎2',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎3',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎4',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'CustomHeader',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
const search = ref<string>('')
|
||||
|
||||
function handleEdit(index: number, row: any) {
|
||||
console.log(index, row)
|
||||
}
|
||||
function handleDelete(index: number, row: any) {
|
||||
console.log(index, row)
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
search,
|
||||
handleEdit, handleDelete
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,198 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table :data-source="data" :columns="columns">
|
||||
<template #filterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
|
||||
<div style="padding: 8px">
|
||||
<a-input
|
||||
:ref="c => (searchInput = c)"
|
||||
v-model:value="selectedKeys[0]"
|
||||
:placeholder="`Search ${column.dataIndex}`"
|
||||
style="width: 188px; margin-bottom: 8px; display: block;"
|
||||
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
|
||||
@pressEnter="handleSearch(selectedKeys, confirm, column.dataIndex)"
|
||||
/>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
style="width: 90px; margin-right: 8px"
|
||||
@click="handleSearch(selectedKeys, confirm, column.dataIndex)"
|
||||
>
|
||||
<template #icon><SearchOutlined /></template>
|
||||
Search
|
||||
</a-button>
|
||||
<a-button size="small" style="width: 90px" @click="handleReset(clearFilters)">
|
||||
Reset
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #filterIcon="filtered">
|
||||
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
|
||||
</template>
|
||||
<template #customRender="{ text, column }">
|
||||
<span v-if="searchText && searchedColumn === column.dataIndex">
|
||||
<template
|
||||
v-for="(fragment, i) in text
|
||||
.toString()
|
||||
.split(new RegExp(`(?<=${searchText})|(?=${searchText})`, 'i'))"
|
||||
:key="i"
|
||||
>
|
||||
<mark v-if="fragment.toLowerCase() === searchText.toLowerCase()" class="highlight">
|
||||
{{ fragment }}
|
||||
</mark>
|
||||
<template v-else>{{ fragment }}</template>
|
||||
</template>
|
||||
</span>
|
||||
<template v-else>
|
||||
{{ text }}
|
||||
</template>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, getCurrentInstance } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
import { SearchOutlined } from '@ant-design/icons-vue'
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Joe Black',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Jim Green',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: 'Jim Red',
|
||||
age: 32,
|
||||
address: 'London No. 2 Lake Park'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'CustomMenu',
|
||||
components: {
|
||||
ComTable,
|
||||
SearchOutlined
|
||||
},
|
||||
setup() {
|
||||
const { ctx } = getCurrentInstance() as any
|
||||
|
||||
const searchText = ref<string>('')
|
||||
const searchInput = ref<any>(null)
|
||||
const searchedColumn = ref<string>('')
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: {
|
||||
filterDropdown: 'filterDropdown',
|
||||
filterIcon: 'filterIcon',
|
||||
customRender: 'customRender'
|
||||
},
|
||||
onFilter: (value: string, record: any) =>
|
||||
record.name
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: (visible: boolean) => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
console.log(searchInput.value)
|
||||
searchInput.value.focus()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
slots: {
|
||||
filterDropdown: 'filterDropdown',
|
||||
filterIcon: 'filterIcon',
|
||||
customRender: 'customRender'
|
||||
},
|
||||
onFilter: (value: string, record: any) =>
|
||||
record.age
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: (visible: boolean) => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
searchInput.value.focus()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
slots: {
|
||||
filterDropdown: 'filterDropdown',
|
||||
filterIcon: 'filterIcon',
|
||||
customRender: 'customRender'
|
||||
},
|
||||
onFilter: (value: string, record: any) =>
|
||||
record.address
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: (visible: boolean) => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
searchInput.value.focus()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
function handleSearch(selectedKeys: string[], confirm: Function, dataIndex: string) {
|
||||
confirm()
|
||||
searchText.value = selectedKeys[0]
|
||||
searchedColumn.value = dataIndex
|
||||
ctx.$forceUpdate()
|
||||
}
|
||||
|
||||
function handleReset(clearFilters: Function) {
|
||||
clearFilters()
|
||||
searchText.value = ''
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
columns,
|
||||
searchText,
|
||||
searchInput,
|
||||
searchedColumn,
|
||||
handleSearch,
|
||||
handleReset
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@{deep}(.highlight) {
|
||||
background-color: rgb(255, 192, 105);
|
||||
padding: 0px;
|
||||
}
|
||||
</style>
|
|
@ -1,98 +0,0 @@
|
|||
<template>
|
||||
<div class="editable-cell">
|
||||
<div v-if="editable" class="editable-cell-input-wrapper">
|
||||
<a-input v-model:value="value" @pressEnter="check" />
|
||||
<check-outlined class="editable-cell-icon-check" @click="check" />
|
||||
</div>
|
||||
<div v-else class="editable-cell-text-wrapper">
|
||||
{{ value || ' ' }}
|
||||
<edit-outlined class="editable-cell-icon" @click="edit" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { CheckOutlined, EditOutlined } from '@ant-design/icons-vue'
|
||||
import { defineComponent, PropType, ref } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'EditableCell',
|
||||
components: {
|
||||
CheckOutlined,
|
||||
EditOutlined
|
||||
},
|
||||
props: {
|
||||
text: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
onChange: {
|
||||
type: Function as PropType<Function | null>,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const value = ref<string>(props.text)
|
||||
const editable = ref<boolean>(false)
|
||||
|
||||
function handleChange(e: any) {
|
||||
const value = e.target.value
|
||||
value.value = value
|
||||
}
|
||||
function check() {
|
||||
editable.value = false
|
||||
emit('change', value.value)
|
||||
}
|
||||
function edit() {
|
||||
editable.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
value, editable,
|
||||
check, edit
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.editable-cell {
|
||||
position: relative;
|
||||
.editable-cell-input-wrapper,
|
||||
.editable-cell-text-wrapper {
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.editable-cell-text-wrapper {
|
||||
padding: 5px 24px 5px 5px;
|
||||
}
|
||||
|
||||
.editable-cell-icon,
|
||||
.editable-cell-icon-check {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.editable-cell-icon {
|
||||
line-height: 18px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.editable-cell-icon-check {
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.editable-cell-icon:hover,
|
||||
.editable-cell-icon-check:hover {
|
||||
color: #108ee9;
|
||||
}
|
||||
|
||||
.editable-add-btn {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
.editable-cell:hover .editable-cell-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
|
@ -1,104 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-button class="editable-add-btn" @click="handleAdd">
|
||||
Add
|
||||
</a-button>
|
||||
<com-table bordered :data-source="dataSource" :columns="columns">
|
||||
<template #name="{ text, record }">
|
||||
<editable-cell :text="text" @change="val => onCellChange(record.key, 'name', val)" />
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<a-popconfirm
|
||||
v-if="dataSource.length"
|
||||
title="Sure to delete?"
|
||||
@confirm="onDelete(record.key)"
|
||||
>
|
||||
<a>Delete</a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
import EditableCell from './EditableCell.vue'
|
||||
export default defineComponent({
|
||||
// name: 'EditCell',
|
||||
components: {
|
||||
ComTable,
|
||||
EditableCell
|
||||
},
|
||||
setup() {
|
||||
const count = ref<number>(2)
|
||||
const dataSource = ref<any[]>([
|
||||
{
|
||||
key: '0',
|
||||
name: 'Edward King 0',
|
||||
age: '32',
|
||||
address: 'London, Park Lane no. 0'
|
||||
},
|
||||
{
|
||||
key: '1',
|
||||
name: 'Edward King 1',
|
||||
age: '32',
|
||||
address: 'London, Park Lane no. 1'
|
||||
}
|
||||
])
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
title: 'name',
|
||||
dataIndex: 'name',
|
||||
width: '30%',
|
||||
slots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: 'age',
|
||||
dataIndex: 'age'
|
||||
},
|
||||
{
|
||||
title: 'address',
|
||||
dataIndex: 'address'
|
||||
},
|
||||
{
|
||||
title: 'operation',
|
||||
dataIndex: 'operation',
|
||||
slots: { customRender: 'operation' }
|
||||
}
|
||||
])
|
||||
|
||||
function onCellChange(key: string, dataIndex: string, value: string) {
|
||||
const newDataSource = [...dataSource.value]
|
||||
const target: any[] = newDataSource.find((item: any) => item.key === key)
|
||||
if (target) {
|
||||
target[dataIndex] = value
|
||||
dataSource.value = newDataSource
|
||||
}
|
||||
}
|
||||
function onDelete(key: string) {
|
||||
const newDataSource = [...dataSource.value]
|
||||
dataSource.value = newDataSource.filter((item: any) => item.key !== key)
|
||||
}
|
||||
function handleAdd() {
|
||||
const newData = {
|
||||
key: count.value,
|
||||
name: `Edward King ${count.value}`,
|
||||
age: 32,
|
||||
address: `London, Park Lane no. ${count.value}`
|
||||
}
|
||||
dataSource.value = [...dataSource.value, newData]
|
||||
count.value = count.value + 1
|
||||
}
|
||||
|
||||
return {
|
||||
count, dataSource, columns,
|
||||
onCellChange, onDelete, handleAdd
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,141 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table :columns="columns" :data-source="data" bordered>
|
||||
<template v-for="col in ['name', 'age', 'address']" #[col]="{ text, record }" :key="col">
|
||||
<div :key="col">
|
||||
<a-input
|
||||
v-if="record.editable"
|
||||
style="margin: -5px 0"
|
||||
:value="text"
|
||||
@change="e => handleChange(e.target.value, record.key, col)"
|
||||
/>
|
||||
<template v-else>
|
||||
{{ text }}
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<div class="editable-row-operations">
|
||||
<span v-if="record.editable">
|
||||
<a @click="save(record.key)">Save</a>
|
||||
<a-popconfirm title="Sure to cancel?" @confirm="cancel(record.key)">
|
||||
<a>Cancel</a>
|
||||
</a-popconfirm>
|
||||
</span>
|
||||
<span v-else>
|
||||
<a v-bind="editingKey !== '' ? { disabled: 'disabled' } : {}" @click="edit(record.key)">
|
||||
Edit
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'name',
|
||||
dataIndex: 'name',
|
||||
width: '25%',
|
||||
slots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: 'age',
|
||||
dataIndex: 'age',
|
||||
width: '15%',
|
||||
slots: { customRender: 'age' }
|
||||
},
|
||||
{
|
||||
title: 'address',
|
||||
dataIndex: 'address',
|
||||
width: '40%',
|
||||
slots: { customRender: 'address' }
|
||||
},
|
||||
{
|
||||
title: 'operation',
|
||||
dataIndex: 'operation',
|
||||
slots: { customRender: 'operation' }
|
||||
}
|
||||
]
|
||||
|
||||
const dataList: any[] = []
|
||||
for (let i = 0; i < 100; i++) {
|
||||
dataList.push({
|
||||
key: i.toString(),
|
||||
name: `Edrward ${i}`,
|
||||
age: 32,
|
||||
address: `London Park no. ${i}`
|
||||
})
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'EditRow',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const cacheData = ref<any[]>(dataList.map(item => ({ ...item })))
|
||||
const editingKey = ref<string>('')
|
||||
const data = ref<any[]>(dataList)
|
||||
|
||||
function handleChange(value: string, key: string, column: string) {
|
||||
const newData = [...data.value]
|
||||
const target: any[] = newData.filter((item: any) => key === item.key)[0]
|
||||
if (target) {
|
||||
target[column] = value
|
||||
data.value = newData
|
||||
}
|
||||
}
|
||||
function edit(key: string) {
|
||||
const newData = [...data.value]
|
||||
const target: any = newData.filter((item: any) => key === item.key)[0]
|
||||
editingKey.value = key
|
||||
if (target) {
|
||||
target.editable = true
|
||||
data.value = newData
|
||||
}
|
||||
}
|
||||
function save(key: string) {
|
||||
const newData = [...data.value]
|
||||
const newCacheData = [...cacheData.value]
|
||||
const target: any = newData.filter((item: any) => key === item.key)[0]
|
||||
const targetCache: any[] = newCacheData.filter((item: any) => key === item.key)[0]
|
||||
if (target && targetCache) {
|
||||
delete target.editable
|
||||
data.value = newData
|
||||
Object.assign(targetCache, target)
|
||||
cacheData.value = newCacheData
|
||||
}
|
||||
editingKey.value = ''
|
||||
}
|
||||
function cancel(key: string) {
|
||||
const newData = [...data.value]
|
||||
const target: any = newData.filter((item: any) => key === item.key)[0]
|
||||
editingKey.value = ''
|
||||
if (target) {
|
||||
Object.assign(target, cacheData.value.filter((item: any) => key === item.key)[0])
|
||||
delete target.editable
|
||||
data.value = newData
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
data,
|
||||
editingKey,
|
||||
handleChange, edit, save, cancel
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.editable-row-operations a {
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,140 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 展开行"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
>
|
||||
<template #id="scope">
|
||||
<el-form label-position="left" inline class="demo-table-expand">
|
||||
<el-form-item label="商品名称">
|
||||
<span>{{ scope.row.name }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属店铺">
|
||||
<span>{{ scope.row.shop }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品 ID">
|
||||
<span>{{ scope.row.id }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺 ID">
|
||||
<span>{{ scope.row.shopId }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品分类">
|
||||
<span>{{ scope.row.category }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺地址">
|
||||
<span>{{ scope.row.address }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品描述">
|
||||
<span>{{ scope.row.desc }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'id',
|
||||
type: 'expand',
|
||||
slots: {
|
||||
default: 'id'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'id',
|
||||
label: '商品ID'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '商品名称'
|
||||
},
|
||||
{
|
||||
key: 'desc',
|
||||
label: '描述'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
id: '12987122',
|
||||
name: '好滋好味鸡蛋仔',
|
||||
category: '江浙小吃、小吃零食',
|
||||
desc: '荷兰优质淡奶,奶香浓而不腻',
|
||||
address: '上海市普陀区真北路',
|
||||
shop: '王小虎夫妻店',
|
||||
shopId: '10333'
|
||||
}, {
|
||||
id: '12987123',
|
||||
name: '好滋好味鸡蛋仔',
|
||||
category: '江浙小吃、小吃零食',
|
||||
desc: '荷兰优质淡奶,奶香浓而不腻',
|
||||
address: '上海市普陀区真北路',
|
||||
shop: '王小虎夫妻店',
|
||||
shopId: '10333'
|
||||
}, {
|
||||
id: '12987125',
|
||||
name: '好滋好味鸡蛋仔',
|
||||
category: '江浙小吃、小吃零食',
|
||||
desc: '荷兰优质淡奶,奶香浓而不腻',
|
||||
address: '上海市普陀区真北路',
|
||||
shop: '王小虎夫妻店',
|
||||
shopId: '10333'
|
||||
}, {
|
||||
id: '12987126',
|
||||
name: '好滋好味鸡蛋仔',
|
||||
category: '江浙小吃、小吃零食',
|
||||
desc: '荷兰优质淡奶,奶香浓而不腻',
|
||||
address: '上海市普陀区真北路',
|
||||
shop: '王小虎夫妻店',
|
||||
shopId: '10333'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'ExpandRow',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@{deep}(.demo-table-expand) {
|
||||
font-size: 0;
|
||||
label {
|
||||
width: 90px;
|
||||
color: #99a9bf;
|
||||
}
|
||||
.el-form-item {
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,166 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定列和表头"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
border
|
||||
height="250"
|
||||
style="width: 820px;"
|
||||
>
|
||||
<template #action="scope">
|
||||
<el-button type="text" size="small" @click="handleClick(scope.row)">查看</el-button>
|
||||
<el-button type="text" size="small">编辑</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期',
|
||||
fixed: true,
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'province',
|
||||
label: '省份',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
label: '市区',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址',
|
||||
width: '300'
|
||||
},
|
||||
{
|
||||
key: 'zip',
|
||||
label: '邮编',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
label: '操作',
|
||||
width: '100',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'FixedColumnHeader',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
function handleClick(row: any) {
|
||||
console.log(row)
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
handleClick
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,133 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定列"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
border
|
||||
style="width: 820px;"
|
||||
>
|
||||
<template #action="scope">
|
||||
<el-button type="text" size="small" @click="handleClick(scope.row)">查看</el-button>
|
||||
<el-button type="text" size="small">编辑</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期',
|
||||
fixed: true,
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'province',
|
||||
label: '省份',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
label: '市区',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址',
|
||||
width: '300'
|
||||
},
|
||||
{
|
||||
key: 'zip',
|
||||
label: '邮编',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
label: '操作',
|
||||
width: '100',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'FixedColumn',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
function handleClick(row: any) {
|
||||
console.log(row)
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
handleClick
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,46 +1,78 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table :columns="columns" :data-source="data" :scroll="{ x: 1500, y: 300 }">
|
||||
<template #action>
|
||||
<a>action</a>
|
||||
</template>
|
||||
</com-table>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定表头"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
height="250"
|
||||
border
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{ title: 'Full Name', width: 100, dataIndex: 'name', key: 'name', fixed: 'left' },
|
||||
{ title: 'Age', width: 100, dataIndex: 'age', key: 'age', fixed: 'left' },
|
||||
{ title: 'Column 1', dataIndex: 'address', key: '1', width: 6000 },
|
||||
{ title: 'Column 2', dataIndex: 'address', key: '2', width: 150 },
|
||||
{ title: 'Column 3', dataIndex: 'address', key: '3', width: 150 },
|
||||
{ title: 'Column 4', dataIndex: 'address', key: '4', width: 150 },
|
||||
{ title: 'Column 5', dataIndex: 'address', key: '5', width: 150 },
|
||||
{ title: 'Column 6', dataIndex: 'address', key: '6', width: 150 },
|
||||
{ title: 'Column 7', dataIndex: 'address', key: '7', width: 150 },
|
||||
{ title: 'Column 8', dataIndex: 'address', key: '8' },
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'operation',
|
||||
fixed: 'right',
|
||||
width: 100,
|
||||
slots: { customRender: 'action' }
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const data: any[] = []
|
||||
for (let i = 0; i < 100; i++) {
|
||||
data.push({
|
||||
key: i,
|
||||
name: `Edrward ${i}`,
|
||||
age: 32,
|
||||
address: `London Park no. ${i}`
|
||||
})
|
||||
}
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'FixedHeader',
|
||||
|
@ -48,9 +80,15 @@ export default defineComponent({
|
|||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
return {
|
||||
columns,
|
||||
data
|
||||
tableData,
|
||||
loading
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 流体高度"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
border
|
||||
max-height="250"
|
||||
style="width: 820px;"
|
||||
>
|
||||
<template #action="scope">
|
||||
<el-button type="text" size="small" @click="deleteRow(scope.$index)">移除</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期',
|
||||
fixed: true,
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'province',
|
||||
label: '省份',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
label: '市区',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址',
|
||||
width: '300'
|
||||
},
|
||||
{
|
||||
key: 'zip',
|
||||
label: '邮编',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
label: '操作',
|
||||
width: '100',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
export default defineComponent({
|
||||
// name: 'FluidHeight',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const tableData = ref<any[]>([
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
}
|
||||
])
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
function deleteRow(index: number) {
|
||||
tableData.value.splice(index, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
deleteRow
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,162 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 多级表头"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
>
|
||||
<template #address="scope">
|
||||
地址是: {{ scope.row.address }}
|
||||
</template>
|
||||
<template #action="scope">
|
||||
<el-button type="text" size="small" @click="deleteRow(scope.$index)">移除</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期',
|
||||
fixed: true,
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
label: '配送信息',
|
||||
children: [
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
label: '地址',
|
||||
children: [
|
||||
{
|
||||
key: 'province',
|
||||
label: '省份',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
label: '市区',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址',
|
||||
slots: {
|
||||
default: 'address'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'zip',
|
||||
label: '邮编',
|
||||
width: '120'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
label: '操作',
|
||||
width: '100',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'MultiHeader',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const tableData = ref<any[]>([
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
}, {
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
}, {
|
||||
date: '2016-05-08',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
}, {
|
||||
date: '2016-05-06',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
}, {
|
||||
date: '2016-05-07',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
}
|
||||
])
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
function deleteRow(index: number) {
|
||||
tableData.value.splice(index, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
deleteRow
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 多选"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
selection
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
@selection-change="handleSelectionChange"
|
||||
/>
|
||||
|
||||
<div style="margin-top: 20px">
|
||||
<el-button @click="toggleSelection([tableData[1], tableData[2]])">切换第二、第三行的选中状态</el-button>
|
||||
<el-button @click="toggleSelection()">取消选择</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, unref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'MultipleChoice',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
const multipleTable = ref<HTMLElement | null>(null)
|
||||
function toggleSelection(rows: any[]) {
|
||||
const multipleTableRef = unref(multipleTable as any).getTableRef()
|
||||
if (rows) {
|
||||
rows.forEach(row => {
|
||||
multipleTableRef.toggleRowSelection(row)
|
||||
})
|
||||
} else {
|
||||
multipleTableRef.clearSelection()
|
||||
}
|
||||
}
|
||||
function handleSelectionChange(val: any) {
|
||||
console.log(val)
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
multipleTable, toggleSelection, handleSelectionChange
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,130 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 筛选"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<el-button @click="resetDateFilter">清除日期过滤器</el-button>
|
||||
<el-button @click="clearFilter">清除所有过滤器</el-button>
|
||||
<com-table
|
||||
ref="filterTable"
|
||||
v-loading="loading"
|
||||
row-key="date"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:default-sort="{prop: 'date', order: 'descending'}"
|
||||
>
|
||||
<template #tag="scope">
|
||||
<el-tag
|
||||
:type="scope.row.tag === '家' ? 'primary' : 'success'"
|
||||
disable-transitions
|
||||
>{{ scope.row.tag }}</el-tag>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, unref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
tag: '家'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
tag: '公司'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
tag: '家'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
tag: '公司'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'ScreenTable',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const filterTable = ref<HTMLElement | null>(null)
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期',
|
||||
sortable: true,
|
||||
width: '180',
|
||||
columnKey: 'date',
|
||||
filters: [{ text: '2016-05-01', value: '2016-05-01' }, { text: '2016-05-02', value: '2016-05-02' }, { text: '2016-05-03', value: '2016-05-03' }, { text: '2016-05-04', value: '2016-05-04' }],
|
||||
filterMethod: filterHandler
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
},
|
||||
{
|
||||
key: 'tag',
|
||||
label: '标签',
|
||||
filters: [{ text: '家', value: '家' }, { text: '公司', value: '公司' }],
|
||||
filterMethod: filterTag,
|
||||
filterPlacement: 'bottom-end',
|
||||
slots: {
|
||||
default: 'tag'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
function resetDateFilter() {
|
||||
const filterTableRef = unref(filterTable as any).getTableRef()
|
||||
filterTableRef.clearFilter('date')
|
||||
}
|
||||
function clearFilter() {
|
||||
const filterTableRef = unref(filterTable as any).getTableRef()
|
||||
filterTableRef.clearFilter()
|
||||
}
|
||||
function filterTag(value: string, row: any) {
|
||||
return row.tag === value
|
||||
}
|
||||
function filterHandler(value: string, row: any, column: any) {
|
||||
const property = column['property']
|
||||
return row[property] === value
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
filterTable,
|
||||
resetDateFilter, clearFilter, filterTag, filterHandler
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 单选"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
ref="singleTable"
|
||||
v-loading="loading"
|
||||
highlight-current-row
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
|
||||
<div style="margin-top: 20px">
|
||||
<el-button @click="setCurrent(tableData[1])">选中第二行</el-button>
|
||||
<el-button @click="setCurrent()">取消选择</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, unref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'SingleChoice',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
const singleTable = ref<HTMLElement | null>(null)
|
||||
function setCurrent(row: any) {
|
||||
const singleTableRef = unref(singleTable as any).getTableRef()
|
||||
singleTableRef.setCurrentRow(row)
|
||||
}
|
||||
function handleCurrentChange(val: any) {
|
||||
console.log(val)
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
singleTable, setCurrent, handleCurrentChange
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 排序"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:default-sort="{prop: 'date', order: 'descending'}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'SortTable',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带状态表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:row-class-name="tableRowClassName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'StateTable',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
function tableRowClassName({ rowIndex }: any) {
|
||||
if (rowIndex === 1) {
|
||||
return 'warning-row'
|
||||
} else if (rowIndex === 3) {
|
||||
return 'success-row'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading,
|
||||
tableRowClassName
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@{deep}(.el-table) {
|
||||
.warning-row {
|
||||
background: oldlace;
|
||||
}
|
||||
}
|
||||
|
||||
@{deep}(.el-table) {
|
||||
.success-row {
|
||||
background: #f0f9eb;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带斑马纹表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
stripe
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'StripeTable',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
loading
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,87 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table
|
||||
:columns="columns"
|
||||
:data-source="data"
|
||||
bordered
|
||||
>
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
</template>
|
||||
<template #title>
|
||||
Header
|
||||
</template>
|
||||
<template #footer>
|
||||
Footer
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
slots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: 'Cash Assets',
|
||||
className: 'column-money',
|
||||
dataIndex: 'money'
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address'
|
||||
}
|
||||
]
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
money: '¥300,000.00',
|
||||
address: 'New York No. 1 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
money: '¥1,256,000.00',
|
||||
address: 'London No. 1 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Joe Black',
|
||||
money: '¥120,000.00',
|
||||
address: 'Sidney No. 1 Lake Park'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'TableBorder',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
return {
|
||||
loading,
|
||||
columns,
|
||||
data
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@{deep}(th.column-money),
|
||||
@{deep}(td.column-money) {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
|
@ -1,102 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data-source="data"
|
||||
>
|
||||
<template #name="{text}">
|
||||
<a>{{ text }}</a>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address 1',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: 'Long Column Long Column Long Column',
|
||||
dataIndex: 'address',
|
||||
key: 'address 2',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: 'Long Column Long Column',
|
||||
dataIndex: 'address',
|
||||
key: 'address 3',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: 'Long Column',
|
||||
dataIndex: 'address',
|
||||
key: 'address 4',
|
||||
ellipsis: true
|
||||
}
|
||||
]
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park, New York No. 1 Lake Park',
|
||||
tags: ['nice', 'developer']
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 2 Lake Park, London No. 2 Lake Park',
|
||||
tags: ['loser']
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park, Sidney No. 1 Lake Park',
|
||||
tags: ['cool', 'teacher']
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'TableEllipsis',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
return {
|
||||
loading,
|
||||
columns,
|
||||
data
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,66 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table :columns="columns" :data-source="data">
|
||||
<template #action>
|
||||
<a>Delete</a>
|
||||
</template>
|
||||
<template #expandedRowRender="{ record }">
|
||||
<p style="margin: 0">
|
||||
{{ record.description }}
|
||||
</p>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
|
||||
const columns = [
|
||||
{ title: 'Name', dataIndex: 'name', key: 'name' },
|
||||
{ title: 'Age', dataIndex: 'age', key: 'age' },
|
||||
{ title: 'Address', dataIndex: 'address', key: 'address' },
|
||||
{ title: 'Action', dataIndex: '', key: 'x', slots: { customRender: 'action' }}
|
||||
]
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: 1,
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.'
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park',
|
||||
description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.'
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park',
|
||||
description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'TableExpanded',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
columns,
|
||||
data
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,107 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table
|
||||
:columns="columns"
|
||||
:row-key="record => record.sid"
|
||||
:data-source="data"
|
||||
:pagination="pagination"
|
||||
:loading="loading"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #name="{ text }"> {{ text }} </template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
import api from '_p/index/api'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
sorter: true,
|
||||
// sortDirections: ['ascend', 'descend'],
|
||||
width: '20%',
|
||||
slots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: 'Text',
|
||||
dataIndex: 'text',
|
||||
filters: [
|
||||
{ text: 'Male', value: 'male' },
|
||||
{ text: 'Female', value: 'female' }
|
||||
],
|
||||
width: '60%'
|
||||
},
|
||||
{
|
||||
title: 'Passtime',
|
||||
dataIndex: 'passtime'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'TableLoad',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
const data = ref<any[]>([])
|
||||
const pagination = ref<Object>({})
|
||||
|
||||
onMounted(() => {
|
||||
fetch()
|
||||
})
|
||||
|
||||
function handleTableChange(pagination: any, filters: any, sorter: any) {
|
||||
console.log(sorter)
|
||||
const pager: any = { ...pagination }
|
||||
pager.current = pagination.current
|
||||
pagination.value = pager
|
||||
fetch({
|
||||
count: pagination.pageSize,
|
||||
page: pagination.current,
|
||||
sortField: sorter.field,
|
||||
sortOrder: sorter.order,
|
||||
...filters
|
||||
})
|
||||
}
|
||||
|
||||
async function fetch(params = {
|
||||
page: 1,
|
||||
count: 10
|
||||
}) {
|
||||
try {
|
||||
loading.value = true
|
||||
const res = await api.common.getList({
|
||||
params
|
||||
})
|
||||
if (res) {
|
||||
const pager: any = { ...pagination.value }
|
||||
pager.total = 200
|
||||
data.value = res.result
|
||||
pagination.value = pager
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
columns,
|
||||
data,
|
||||
pagination,
|
||||
handleTableChange
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,151 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table
|
||||
:columns="columns"
|
||||
:data-source="data"
|
||||
bordered
|
||||
>
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, h } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
|
||||
const renderContent = ({ text, index }: any) => {
|
||||
const obj: any = {
|
||||
children: text,
|
||||
props: {}
|
||||
}
|
||||
if (index === 4) {
|
||||
obj.props.colSpan = 0
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
name: 'John Brown',
|
||||
age: 32,
|
||||
tel: '0571-22098909',
|
||||
phone: 18889898989,
|
||||
address: 'New York No. 1 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
name: 'Jim Green',
|
||||
tel: '0571-22098333',
|
||||
phone: 18889898888,
|
||||
age: 42,
|
||||
address: 'London No. 1 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
tel: '0575-22098909',
|
||||
phone: 18900010002,
|
||||
address: 'Sidney No. 1 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: 'Jim Red',
|
||||
age: 18,
|
||||
tel: '0575-22098909',
|
||||
phone: 18900010002,
|
||||
address: 'London No. 2 Lake Park'
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
name: 'Jake White',
|
||||
age: 18,
|
||||
tel: '0575-22098909',
|
||||
phone: 18900010002,
|
||||
address: 'Dublin No. 2 Lake Park'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'TableMerge',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
customRender: ({ text, index }: any) => {
|
||||
if (index < 4) {
|
||||
return h('a', {
|
||||
attrs: {
|
||||
href: 'javascript:;'
|
||||
}
|
||||
}, text)
|
||||
}
|
||||
return {
|
||||
children: h('a', {
|
||||
attrs: {
|
||||
href: 'javascript:;'
|
||||
}
|
||||
}, text),
|
||||
props: {
|
||||
colSpan: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
customRender: renderContent
|
||||
},
|
||||
{
|
||||
title: 'Home phone',
|
||||
colSpan: 2,
|
||||
dataIndex: 'tel',
|
||||
customRender: ({ text, index }: any) => {
|
||||
const obj: any = {
|
||||
children: text,
|
||||
props: {}
|
||||
}
|
||||
if (index === 2) {
|
||||
obj.props.rowSpan = 2
|
||||
}
|
||||
// These two are merged into above cell
|
||||
if (index === 3) {
|
||||
obj.props.rowSpan = 0
|
||||
}
|
||||
if (index === 4) {
|
||||
obj.props.colSpan = 0
|
||||
}
|
||||
return obj
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Phone',
|
||||
colSpan: 0,
|
||||
dataIndex: 'phone',
|
||||
customRender: renderContent
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
customRender: renderContent
|
||||
}
|
||||
])
|
||||
return {
|
||||
data,
|
||||
columns
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,124 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<com-table :columns="columns" :data-source="data" :row-selection="rowSelection" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import ComTable from '_c/Table'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
width: '12%'
|
||||
},
|
||||
{
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
width: '30%',
|
||||
key: 'address'
|
||||
}
|
||||
]
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: 1,
|
||||
name: 'John Brown sr.',
|
||||
age: 60,
|
||||
address: 'New York No. 1 Lake Park',
|
||||
children: [
|
||||
{
|
||||
key: 11,
|
||||
name: 'John Brown',
|
||||
age: 42,
|
||||
address: 'New York No. 2 Lake Park'
|
||||
},
|
||||
{
|
||||
key: 12,
|
||||
name: 'John Brown jr.',
|
||||
age: 30,
|
||||
address: 'New York No. 3 Lake Park',
|
||||
children: [
|
||||
{
|
||||
key: 121,
|
||||
name: 'Jimmy Brown',
|
||||
age: 16,
|
||||
address: 'New York No. 3 Lake Park'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 13,
|
||||
name: 'Jim Green sr.',
|
||||
age: 72,
|
||||
address: 'London No. 1 Lake Park',
|
||||
children: [
|
||||
{
|
||||
key: 131,
|
||||
name: 'Jim Green',
|
||||
age: 42,
|
||||
address: 'London No. 2 Lake Park',
|
||||
children: [
|
||||
{
|
||||
key: 1311,
|
||||
name: 'Jim Green jr.',
|
||||
age: 25,
|
||||
address: 'London No. 3 Lake Park'
|
||||
},
|
||||
{
|
||||
key: 1312,
|
||||
name: 'Jimmy Green sr.',
|
||||
age: 18,
|
||||
address: 'London No. 4 Lake Park'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
name: 'Joe Black',
|
||||
age: 32,
|
||||
address: 'Sidney No. 1 Lake Park'
|
||||
}
|
||||
]
|
||||
|
||||
const rowSelection = {
|
||||
onChange: (selectedRowKeys: any, selectedRows: any) => {
|
||||
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows)
|
||||
},
|
||||
onSelect: (record: any, selected: any, selectedRows: any) => {
|
||||
console.log(record, selected, selectedRows)
|
||||
},
|
||||
onSelectAll: (selected: any, selectedRows: any, changeRows: any) => {
|
||||
console.log(selected, selectedRows, changeRows)
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'TableTree',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
columns,
|
||||
data,
|
||||
rowSelection
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -10,7 +10,7 @@
|
|||
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
|
||||
@pressEnter="handleSearch(selectedKeys, confirm, column.dataIndex)"
|
||||
/>
|
||||
<a-button
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
style="width: 90px; margin-right: 8px"
|
||||
|
@ -18,10 +18,10 @@
|
|||
>
|
||||
<template #icon><SearchOutlined /></template>
|
||||
Search
|
||||
</a-button>
|
||||
<a-button size="small" style="width: 90px" @click="handleReset(clearFilters)">
|
||||
</el-button>
|
||||
<el-button size="small" style="width: 90px" @click="handleReset(clearFilters)">
|
||||
Reset
|
||||
</a-button>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #filterIcon="filtered">
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 表尾合计行"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
border
|
||||
show-summary
|
||||
/>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns1"
|
||||
:data="tableData"
|
||||
border
|
||||
height="200"
|
||||
:summary-method="getSummaries"
|
||||
show-summary
|
||||
style="margin-top: 20px;"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'id',
|
||||
label: 'ID'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'amount1',
|
||||
label: '数值1',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'amount2',
|
||||
label: '数值2',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'amount3',
|
||||
label: '数值4',
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns1 = [
|
||||
{
|
||||
key: 'id',
|
||||
label: 'ID'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'amount1',
|
||||
label: '数值1(元)'
|
||||
},
|
||||
{
|
||||
key: 'amount2',
|
||||
label: '数值2(元)'
|
||||
},
|
||||
{
|
||||
key: 'amount3',
|
||||
label: '数值4(元)'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
id: '12987122',
|
||||
name: '王小虎',
|
||||
amount1: '234',
|
||||
amount2: '3.2',
|
||||
amount3: 10
|
||||
}, {
|
||||
id: '12987123',
|
||||
name: '王小虎',
|
||||
amount1: '165',
|
||||
amount2: '4.43',
|
||||
amount3: 12
|
||||
}, {
|
||||
id: '12987124',
|
||||
name: '王小虎',
|
||||
amount1: '324',
|
||||
amount2: '1.9',
|
||||
amount3: 9
|
||||
}, {
|
||||
id: '12987125',
|
||||
name: '王小虎',
|
||||
amount1: '621',
|
||||
amount2: '2.2',
|
||||
amount3: 17
|
||||
}, {
|
||||
id: '12987126',
|
||||
name: '王小虎',
|
||||
amount1: '539',
|
||||
amount2: '4.1',
|
||||
amount3: 15
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'TotalTable',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
function getSummaries(param: any) {
|
||||
const { columns, data } = param
|
||||
const sums: any[] = []
|
||||
columns.forEach((column: any, index: number) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '总价'
|
||||
return
|
||||
}
|
||||
const values = data.map((item: any) => Number(item[column.property]))
|
||||
if (!values.every((value: number) => isNaN(value))) {
|
||||
sums[index] = values.reduce((prev: number, curr: number) => {
|
||||
const value = Number(curr)
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr
|
||||
} else {
|
||||
return prev
|
||||
}
|
||||
}, 0)
|
||||
sums[index] += ' 元'
|
||||
} else {
|
||||
sums[index] = 'N/A'
|
||||
}
|
||||
})
|
||||
|
||||
return sums
|
||||
}
|
||||
|
||||
return {
|
||||
columns, columns1,
|
||||
tableData,
|
||||
loading,
|
||||
getSummaries
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,181 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 树形数据与懒加载"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
row-key="id"
|
||||
border
|
||||
default-expand-all
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
/>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns1"
|
||||
:data="tableData1"
|
||||
row-key="id"
|
||||
border
|
||||
lazy
|
||||
:load="load"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
style="margin-top: 20px;"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import ComTable from '_c/Table/index.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const columns1 = [
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
id: 1,
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
children: [
|
||||
{
|
||||
id: 31,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData1 = [
|
||||
{
|
||||
id: 1,
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'TreeAndLoad',
|
||||
components: {
|
||||
ComTable
|
||||
},
|
||||
setup() {
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
|
||||
function load(tree: any, treeNode: any, resolve: Function) {
|
||||
setTimeout(() => {
|
||||
resolve([
|
||||
{
|
||||
id: 31,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}, {
|
||||
id: 32,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}
|
||||
])
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
columns1,
|
||||
tableData,
|
||||
tableData1,
|
||||
loading,
|
||||
load
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -1,234 +0,0 @@
|
|||
#app {
|
||||
|
||||
// 主体区域 Main container
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left .28s;
|
||||
margin-left: @menuWidth;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// 侧边栏 Sidebar container
|
||||
.sidebar-container {
|
||||
transition: width 0.28s;
|
||||
width: @menuWidth !important;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
font-size: 0px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
|
||||
//reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
|
||||
}
|
||||
|
||||
.scrollbar-wrapper {
|
||||
overflow-x: hidden !important;
|
||||
|
||||
.el-scrollbar__view {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.el-scrollbar__bar.is-vertical {
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.has-logo {
|
||||
.el-scrollbar {
|
||||
height: calc(100% - 70px);
|
||||
}
|
||||
}
|
||||
|
||||
.is-horizontal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-submenu__title {
|
||||
color: hsla(0,0%,100%,.7) !important;
|
||||
&:hover {
|
||||
// background-color: @menuHover !important;
|
||||
color: @subMenuActiveText !important;
|
||||
}
|
||||
}
|
||||
|
||||
.is-active>.el-submenu__title {
|
||||
color: @subMenuActiveText !important;
|
||||
}
|
||||
|
||||
.is-active {
|
||||
color: @subMenuActiveText !important;
|
||||
background-color: @menuActiveBg !important;
|
||||
&:hover {
|
||||
color: @subMenuActiveText !important;
|
||||
background-color: @menuActiveBg !important;
|
||||
}
|
||||
& .el-menu-item {
|
||||
background-color: @menuActiveBg !important;
|
||||
&:hover {
|
||||
color: @subMenuActiveText !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .nest-menu .el-submenu>.el-submenu__title,
|
||||
& .el-submenu .el-menu-item {
|
||||
min-width: @menuWidth !important;
|
||||
background-color: @subMenuBg !important;
|
||||
|
||||
&:hover {
|
||||
color: @subMenuActiveText !important;
|
||||
background-color: @subMenuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .nest-menu {
|
||||
& .is-active {
|
||||
background-color: @menuActiveBg !important;
|
||||
&:hover {
|
||||
color: @subMenuActiveText !important;
|
||||
background-color: @menuActiveBg !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 36px !important;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
margin-left: 36px;
|
||||
}
|
||||
|
||||
.submenu-title-noDropdown {
|
||||
padding-left: 10px !important;
|
||||
position: relative;
|
||||
|
||||
.el-tooltip {
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu {
|
||||
overflow: hidden;
|
||||
|
||||
&>.el-submenu__title {
|
||||
padding-left: 10px !important;
|
||||
|
||||
.el-submenu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse {
|
||||
.el-submenu {
|
||||
&>.el-submenu__title {
|
||||
&>span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse .el-menu .el-submenu {
|
||||
min-width: @menuWidth !important;
|
||||
}
|
||||
|
||||
// 适配移动端, Mobile responsive
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: transform .28s;
|
||||
width: @menuWidth !important;
|
||||
}
|
||||
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(-@menuWidth, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.withoutAnimation {
|
||||
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when menu collapsed
|
||||
.el-menu--vertical {
|
||||
&>.el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.nest-menu .el-submenu>.el-submenu__title,
|
||||
.el-menu-item {
|
||||
&:hover {
|
||||
// you can use @subMenuHover
|
||||
// background-color: @menuHover !important;
|
||||
}
|
||||
}
|
||||
|
||||
// the scroll bar appears when the subMenu is too long
|
||||
>.el-menu--popup {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,24 +35,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-active {
|
||||
color: @menuActiveText !important;
|
||||
background-color: @subMenuHover !important;
|
||||
&>.el-submenu__title {
|
||||
color: @menuActiveText !important;
|
||||
}
|
||||
}
|
||||
|
||||
.nest-menu {
|
||||
background-color: @subMenuBg !important;
|
||||
.el-submenu>.el-submenu__title {
|
||||
background-color: @subMenuBg !important;
|
||||
}
|
||||
.is-active {
|
||||
background-color: @subMenuHover !important;
|
||||
}
|
||||
}
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-submenu__title {
|
||||
|
@ -65,6 +47,28 @@
|
|||
color: @subMenuActiveText !important;
|
||||
// background-color: @subMenuHover !important;
|
||||
}
|
||||
.is-active {
|
||||
color: @menuActiveText !important;
|
||||
background-color: @subMenuHover !important;
|
||||
&:hover {
|
||||
color: @subMenuActiveText !important;
|
||||
background-color: @subMenuHover !important;
|
||||
}
|
||||
&>.el-submenu__title {
|
||||
color: @menuActiveText !important;
|
||||
}
|
||||
}
|
||||
|
||||
// .nest-menu {
|
||||
// background-color: @subMenuBg !important;
|
||||
// .el-submenu>.el-submenu__title {
|
||||
// background-color: @subMenuBg !important;
|
||||
// }
|
||||
// .is-active {
|
||||
// background-color: @subMenuHover !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
.el-menu--collapse {
|
||||
&>div>.el-submenu {
|
||||
|
@ -103,11 +107,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.nest-menu {
|
||||
.is-active {
|
||||
background-color: @subMenuHover !important;
|
||||
}
|
||||
}
|
||||
// .nest-menu {
|
||||
// .is-active {
|
||||
// background-color: @subMenuHover !important;
|
||||
// }
|
||||
// }
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-submenu__title {
|
||||
|
@ -149,15 +153,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
.nest-menu {
|
||||
background-color: @subMenuBg !important;
|
||||
.el-submenu>.el-submenu__title {
|
||||
background-color: @subMenuBg !important;
|
||||
}
|
||||
.is-active {
|
||||
background-color: @subMenuHover !important;
|
||||
}
|
||||
}
|
||||
// .nest-menu {
|
||||
// background-color: @subMenuBg !important;
|
||||
// .el-submenu>.el-submenu__title {
|
||||
// background-color: @subMenuBg !important;
|
||||
// }
|
||||
// .is-active {
|
||||
// background-color: @subMenuHover !important;
|
||||
// }
|
||||
// }
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-submenu__title {
|
||||
|
@ -191,11 +195,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.nest-menu {
|
||||
.is-active {
|
||||
// background-color: @subMenuHover !important;
|
||||
}
|
||||
}
|
||||
// .nest-menu {
|
||||
// .is-active {
|
||||
// // background-color: @subMenuHover !important;
|
||||
// }
|
||||
// }
|
||||
// menu hover
|
||||
.submenu-title-noDropdown,
|
||||
.el-submenu__title {
|
||||
|
|
|
@ -3990,10 +3990,10 @@ electron-to-chromium@^1.3.621:
|
|||
resolved "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.622.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.622.tgz#9726bd2e67a5462154750ce9701ca6af07d07877"
|
||||
integrity sha1-lya9LmelRiFUdQzpcBymrwfQeHc=
|
||||
|
||||
element-plus@^1.0.1-beta.8:
|
||||
version "1.0.1-beta.8"
|
||||
resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.0.1-beta.8.tgz#d64ead88cff37274262bfd9196fe4657f55149ed"
|
||||
integrity sha512-D1u65HYMnT44U3cmll66W4mEhxLPZTqcziJtaWwdtl6VIEjrUwnrkCScnH5mVw3w/3NwRHW6XiqZCFxqafUvhw==
|
||||
element-plus@1.0.1-beta.10:
|
||||
version "1.0.1-beta.10"
|
||||
resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.0.1-beta.10.tgz#3fdc914c4a888b899d701642e13d1904ccae01f0"
|
||||
integrity sha512-yjiU9Nnt8VyKJR6L2JoSa59W9pMX4A/Q6csXbd3OJ2ZcakalCP1iDGhAWmp6WQOkgmJFhhhyLENDd5HH1d5Zdw==
|
||||
dependencies:
|
||||
"@popperjs/core" "^2.4.4"
|
||||
async-validator "^3.4.0"
|
||||
|
|
Loading…
Reference in New Issue