feat: 简易版本
This commit is contained in:
parent
5fc57bdb08
commit
255843d4c0
|
@ -1,3 +0,0 @@
|
|||
import CountTo from './src/CountTo.vue'
|
||||
|
||||
export { CountTo }
|
|
@ -1,180 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { reactive, computed, watch, onMounted, unref, toRef, PropType } from 'vue'
|
||||
import { isNumber } from '@/utils/is'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('count-to')
|
||||
|
||||
const props = defineProps({
|
||||
startVal: propTypes.number.def(0),
|
||||
endVal: propTypes.number.def(2021),
|
||||
duration: propTypes.number.def(3000),
|
||||
autoplay: propTypes.bool.def(true),
|
||||
decimals: propTypes.number.validate((value: number) => value >= 0).def(0),
|
||||
decimal: propTypes.string.def('.'),
|
||||
separator: propTypes.string.def(','),
|
||||
prefix: propTypes.string.def(''),
|
||||
suffix: propTypes.string.def(''),
|
||||
useEasing: propTypes.bool.def(true),
|
||||
easingFn: {
|
||||
type: Function as PropType<(t: number, b: number, c: number, d: number) => number>,
|
||||
default(t: number, b: number, c: number, d: number) {
|
||||
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['mounted', 'callback'])
|
||||
|
||||
const formatNumber = (num: number | string) => {
|
||||
const { decimals, decimal, separator, suffix, prefix } = props
|
||||
num = Number(num).toFixed(decimals)
|
||||
num += ''
|
||||
const x = num.split('.')
|
||||
let x1 = x[0]
|
||||
const x2 = x.length > 1 ? decimal + x[1] : ''
|
||||
const rgx = /(\d+)(\d{3})/
|
||||
if (separator && !isNumber(separator)) {
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + separator + '$2')
|
||||
}
|
||||
}
|
||||
return prefix + x1 + x2 + suffix
|
||||
}
|
||||
|
||||
const state = reactive<{
|
||||
localStartVal: number
|
||||
printVal: number | null
|
||||
displayValue: string
|
||||
paused: boolean
|
||||
localDuration: number | null
|
||||
startTime: number | null
|
||||
timestamp: number | null
|
||||
rAF: any
|
||||
remaining: number | null
|
||||
}>({
|
||||
localStartVal: props.startVal,
|
||||
displayValue: formatNumber(props.startVal),
|
||||
printVal: null,
|
||||
paused: false,
|
||||
localDuration: props.duration,
|
||||
startTime: null,
|
||||
timestamp: null,
|
||||
remaining: null,
|
||||
rAF: null
|
||||
})
|
||||
|
||||
const displayValue = toRef(state, 'displayValue')
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autoplay) {
|
||||
start()
|
||||
}
|
||||
emit('mounted')
|
||||
})
|
||||
|
||||
const getCountDown = computed(() => {
|
||||
return props.startVal > props.endVal
|
||||
})
|
||||
|
||||
watch([() => props.startVal, () => props.endVal], () => {
|
||||
if (props.autoplay) {
|
||||
start()
|
||||
}
|
||||
})
|
||||
|
||||
const start = () => {
|
||||
const { startVal, duration } = props
|
||||
state.localStartVal = startVal
|
||||
state.startTime = null
|
||||
state.localDuration = duration
|
||||
state.paused = false
|
||||
state.rAF = requestAnimationFrame(count)
|
||||
}
|
||||
|
||||
const pauseResume = () => {
|
||||
if (state.paused) {
|
||||
resume()
|
||||
state.paused = false
|
||||
} else {
|
||||
pause()
|
||||
state.paused = true
|
||||
}
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
cancelAnimationFrame(state.rAF)
|
||||
}
|
||||
|
||||
const resume = () => {
|
||||
state.startTime = null
|
||||
state.localDuration = +(state.remaining as number)
|
||||
state.localStartVal = +(state.printVal as number)
|
||||
requestAnimationFrame(count)
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
state.startTime = null
|
||||
cancelAnimationFrame(state.rAF)
|
||||
state.displayValue = formatNumber(props.startVal)
|
||||
}
|
||||
|
||||
const count = (timestamp: number) => {
|
||||
const { useEasing, easingFn, endVal } = props
|
||||
if (!state.startTime) state.startTime = timestamp
|
||||
state.timestamp = timestamp
|
||||
const progress = timestamp - state.startTime
|
||||
state.remaining = (state.localDuration as number) - progress
|
||||
if (useEasing) {
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal =
|
||||
state.localStartVal -
|
||||
easingFn(progress, 0, state.localStartVal - endVal, state.localDuration as number)
|
||||
} else {
|
||||
state.printVal = easingFn(
|
||||
progress,
|
||||
state.localStartVal,
|
||||
endVal - state.localStartVal,
|
||||
state.localDuration as number
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal =
|
||||
state.localStartVal -
|
||||
(state.localStartVal - endVal) * (progress / (state.localDuration as number))
|
||||
} else {
|
||||
state.printVal =
|
||||
state.localStartVal +
|
||||
(endVal - state.localStartVal) * (progress / (state.localDuration as number))
|
||||
}
|
||||
}
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal = state.printVal < endVal ? endVal : state.printVal
|
||||
} else {
|
||||
state.printVal = state.printVal > endVal ? endVal : state.printVal
|
||||
}
|
||||
state.displayValue = formatNumber(state.printVal!)
|
||||
if (progress < (state.localDuration as number)) {
|
||||
state.rAF = requestAnimationFrame(count)
|
||||
} else {
|
||||
emit('callback')
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
pauseResume,
|
||||
reset,
|
||||
start,
|
||||
pause
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span :class="prefixCls">
|
||||
{{ displayValue }}
|
||||
</span>
|
||||
</template>
|
|
@ -1,3 +0,0 @@
|
|||
import Echart from './src/Echart.vue'
|
||||
|
||||
export { Echart }
|
|
@ -1,113 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import echarts from '@/plugins/echarts'
|
||||
import { debounce } from 'lodash-es'
|
||||
import 'echarts-wordcloud'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { computed, PropType, ref, unref, watch, onMounted, onBeforeUnmount, onActivated } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { isString } from '@/utils/is'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls, variables } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('echart')
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Object as PropType<EChartsOption>,
|
||||
required: true
|
||||
},
|
||||
width: propTypes.oneOfType([Number, String]).def(''),
|
||||
height: propTypes.oneOfType([Number, String]).def('500px')
|
||||
})
|
||||
|
||||
const isDark = computed(() => appStore.getIsDark)
|
||||
|
||||
const theme = computed(() => {
|
||||
const echartTheme: boolean | string = unref(isDark) ? true : 'auto'
|
||||
|
||||
return echartTheme
|
||||
})
|
||||
|
||||
const options = computed(() => {
|
||||
return Object.assign(props.options, {
|
||||
darkMode: unref(theme)
|
||||
})
|
||||
})
|
||||
|
||||
const elRef = ref<ElRef>()
|
||||
|
||||
let echartRef: Nullable<echarts.ECharts> = null
|
||||
|
||||
const contentEl = ref<Element>()
|
||||
|
||||
const styles = computed(() => {
|
||||
const width = isString(props.width) ? props.width : `${props.width}px`
|
||||
const height = isString(props.height) ? props.height : `${props.height}px`
|
||||
|
||||
return {
|
||||
width,
|
||||
height
|
||||
}
|
||||
})
|
||||
|
||||
const initChart = () => {
|
||||
if (unref(elRef) && props.options) {
|
||||
echartRef = echarts.init(unref(elRef) as HTMLElement)
|
||||
echartRef?.setOption(unref(options))
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => options.value,
|
||||
(options) => {
|
||||
if (echartRef) {
|
||||
echartRef?.setOption(options)
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
const resizeHandler = debounce(() => {
|
||||
if (echartRef) {
|
||||
echartRef.resize()
|
||||
}
|
||||
}, 100)
|
||||
|
||||
const contentResizeHandler = async (e: TransitionEvent) => {
|
||||
if (e.propertyName === 'width') {
|
||||
resizeHandler()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
|
||||
window.addEventListener('resize', resizeHandler)
|
||||
|
||||
contentEl.value = document.getElementsByClassName(`${variables.namespace}-layout-content`)[0]
|
||||
unref(contentEl) &&
|
||||
(unref(contentEl) as Element).addEventListener('transitionend', contentResizeHandler)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', resizeHandler)
|
||||
unref(contentEl) &&
|
||||
(unref(contentEl) as Element).removeEventListener('transitionend', contentResizeHandler)
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
if (echartRef) {
|
||||
echartRef.resize()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="elRef" :class="[$attrs.class, prefixCls]" :style="styles"></div>
|
||||
</template>
|
|
@ -1,3 +0,0 @@
|
|||
import Highlight from './src/Highlight.vue'
|
||||
|
||||
export { Highlight }
|
|
@ -1,65 +0,0 @@
|
|||
<script lang="tsx">
|
||||
import { defineComponent, PropType, computed, h, unref } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Highlight',
|
||||
props: {
|
||||
tag: propTypes.string.def('span'),
|
||||
keys: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
color: propTypes.string.def('var(--el-color-primary)')
|
||||
},
|
||||
emits: ['click'],
|
||||
setup(props, { emit, slots }) {
|
||||
const keyNodes = computed(() => {
|
||||
return props.keys.map((key) => {
|
||||
return h(
|
||||
'span',
|
||||
{
|
||||
onClick: () => {
|
||||
emit('click', key)
|
||||
},
|
||||
style: {
|
||||
color: props.color,
|
||||
cursor: 'pointer'
|
||||
}
|
||||
},
|
||||
key
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const parseText = (text: string) => {
|
||||
props.keys.forEach((key, index) => {
|
||||
const regexp = new RegExp(key, 'g')
|
||||
text = text.replace(regexp, `{{${index}}}`)
|
||||
})
|
||||
return text.split(/{{|}}/)
|
||||
}
|
||||
|
||||
const renderText = () => {
|
||||
if (!slots?.default) return null
|
||||
const node = slots?.default()[0].children
|
||||
|
||||
if (!node) {
|
||||
return slots?.default()[0]
|
||||
}
|
||||
|
||||
const textArray = parseText(node as string)
|
||||
const regexp = /^[0-9]*$/
|
||||
const nodes = textArray.map((t) => {
|
||||
if (regexp.test(t)) {
|
||||
return unref(keyNodes)[t] || t
|
||||
}
|
||||
return t
|
||||
})
|
||||
return h(props.tag, nodes)
|
||||
}
|
||||
|
||||
return () => renderText()
|
||||
}
|
||||
})
|
||||
</script>
|
|
@ -1,33 +0,0 @@
|
|||
import ImageViewer from './src/ImageViewer.vue'
|
||||
import { isClient } from '@/utils/is'
|
||||
import { createVNode, render, VNode } from 'vue'
|
||||
import { ImageViewerProps } from './src/types'
|
||||
|
||||
let instance: Nullable<VNode> = null
|
||||
|
||||
export function createImageViewer(options: ImageViewerProps) {
|
||||
if (!isClient) return
|
||||
const {
|
||||
urlList,
|
||||
initialIndex = 0,
|
||||
infinite = true,
|
||||
hideOnClickModal = false,
|
||||
teleported = false,
|
||||
zIndex = 2000,
|
||||
show = true
|
||||
} = options
|
||||
|
||||
const propsData: Partial<ImageViewerProps> = {}
|
||||
const container = document.createElement('div')
|
||||
propsData.urlList = urlList
|
||||
propsData.initialIndex = initialIndex
|
||||
propsData.infinite = infinite
|
||||
propsData.hideOnClickModal = hideOnClickModal
|
||||
propsData.teleported = teleported
|
||||
propsData.zIndex = zIndex
|
||||
propsData.show = show
|
||||
|
||||
document.body.appendChild(container)
|
||||
instance = createVNode(ImageViewer, propsData)
|
||||
render(instance, container)
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ElImageViewer } from 'element-plus'
|
||||
import { computed, ref, PropType } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
const props = defineProps({
|
||||
urlList: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => []
|
||||
},
|
||||
zIndex: propTypes.number.def(200),
|
||||
initialIndex: propTypes.number.def(0),
|
||||
infinite: propTypes.bool.def(true),
|
||||
hideOnClickModal: propTypes.bool.def(false),
|
||||
teleported: propTypes.bool.def(false),
|
||||
show: propTypes.bool.def(false)
|
||||
})
|
||||
|
||||
const getBindValue = computed(() => {
|
||||
const propsData: Recordable = { ...props }
|
||||
delete propsData.show
|
||||
return propsData
|
||||
})
|
||||
|
||||
const show = ref(props.show)
|
||||
|
||||
const close = () => {
|
||||
show.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElImageViewer v-if="show" v-bind="getBindValue" @close="close" />
|
||||
</template>
|
|
@ -1,9 +0,0 @@
|
|||
export interface ImageViewerProps {
|
||||
urlList?: string[]
|
||||
zIndex?: number
|
||||
initialIndex?: number
|
||||
infinite?: boolean
|
||||
hideOnClickModal?: boolean
|
||||
teleported?: boolean
|
||||
show?: boolean
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import Infotip from './src/Infotip.vue'
|
||||
|
||||
export type { InfoTipSchema } from './src/types'
|
||||
|
||||
export { Infotip }
|
|
@ -1,53 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
import { Highlight } from '@/components/Highlight'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { InfoTipSchema } from './types'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('infotip')
|
||||
|
||||
defineProps({
|
||||
title: propTypes.string.def(''),
|
||||
schema: {
|
||||
type: Array as PropType<Array<string | InfoTipSchema>>,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
showIndex: propTypes.bool.def(true),
|
||||
highlightColor: propTypes.string.def('var(--el-color-primary)')
|
||||
})
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const keyClick = (key: string) => {
|
||||
emit('click', key)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
prefixCls,
|
||||
'p-20px mb-20px border-1px border-solid border-[var(--el-color-primary)] bg-[var(--el-color-primary-light-9)]'
|
||||
]"
|
||||
>
|
||||
<div v-if="title" :class="[`${prefixCls}__header`, 'flex items-center']">
|
||||
<Icon icon="bi:exclamation-circle-fill" :size="22" color="var(--el-color-primary)" />
|
||||
<span :class="[`${prefixCls}__title`, 'pl-5px text-16px font-bold']">{{ title }}</span>
|
||||
</div>
|
||||
<div :class="`${prefixCls}__content`">
|
||||
<p v-for="(item, $index) in schema" :key="$index" class="text-14px mt-15px">
|
||||
<Highlight
|
||||
:keys="typeof item === 'string' ? [] : item.keys"
|
||||
:color="highlightColor"
|
||||
@click="keyClick"
|
||||
>
|
||||
{{ showIndex ? `${$index + 1}、` : '' }}{{ typeof item === 'string' ? item : item.label }}
|
||||
</Highlight>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,4 +0,0 @@
|
|||
export interface InfoTipSchema {
|
||||
label: string
|
||||
keys?: string[]
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import Qrcode from './src/Qrcode.vue'
|
||||
|
||||
export type { QrcodeLogo } from './src/types'
|
||||
|
||||
export { Qrcode }
|
|
@ -1,252 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { PropType, nextTick, ref, watch, computed, unref } from 'vue'
|
||||
import QRCode from 'qrcode'
|
||||
import { QRCodeRenderersOptions } from 'qrcode'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { isString } from '@/utils/is'
|
||||
import { QrcodeLogo } from '@/components/Qrcode'
|
||||
|
||||
const props = defineProps({
|
||||
// img 或者 canvas,img不支持logo嵌套
|
||||
tag: propTypes.string.validate((v: string) => ['canvas', 'img'].includes(v)).def('canvas'),
|
||||
// 二维码内容
|
||||
text: {
|
||||
type: [String, Array] as PropType<string | Recordable[]>,
|
||||
default: null
|
||||
},
|
||||
// qrcode.js配置项
|
||||
options: {
|
||||
type: Object as PropType<QRCodeRenderersOptions>,
|
||||
default: () => ({})
|
||||
},
|
||||
// 宽度
|
||||
width: propTypes.number.def(200),
|
||||
// logo
|
||||
logo: {
|
||||
type: [String, Object] as PropType<Partial<QrcodeLogo> | string>,
|
||||
default: ''
|
||||
},
|
||||
// 是否过期
|
||||
disabled: propTypes.bool.def(false),
|
||||
// 过期提示内容
|
||||
disabledText: propTypes.string.def('')
|
||||
})
|
||||
|
||||
const emit = defineEmits(['done', 'click', 'disabled-click'])
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('qrcode')
|
||||
|
||||
const { toCanvas, toDataURL } = QRCode
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const wrapRef = ref<Nullable<HTMLCanvasElement | HTMLImageElement>>(null)
|
||||
|
||||
const renderText = computed(() => String(props.text))
|
||||
|
||||
const wrapStyle = computed(() => {
|
||||
return {
|
||||
width: props.width + 'px',
|
||||
height: props.width + 'px'
|
||||
}
|
||||
})
|
||||
|
||||
const initQrcode = async () => {
|
||||
await nextTick()
|
||||
const options = cloneDeep(props.options || {})
|
||||
if (props.tag === 'canvas') {
|
||||
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
|
||||
options.errorCorrectionLevel =
|
||||
options.errorCorrectionLevel || getErrorCorrectionLevel(unref(renderText))
|
||||
const _width: number = await getOriginWidth(unref(renderText), options)
|
||||
options.scale = props.width === 0 ? undefined : (props.width / _width) * 4
|
||||
const canvasRef = (await toCanvas(
|
||||
unref(wrapRef) as HTMLCanvasElement,
|
||||
unref(renderText),
|
||||
options
|
||||
)) as unknown as HTMLCanvasElement
|
||||
if (props.logo) {
|
||||
const url = await createLogoCode(canvasRef)
|
||||
emit('done', url)
|
||||
loading.value = false
|
||||
} else {
|
||||
emit('done', canvasRef.toDataURL())
|
||||
loading.value = false
|
||||
}
|
||||
} else {
|
||||
const url = await toDataURL(renderText.value, {
|
||||
errorCorrectionLevel: 'H',
|
||||
width: props.width,
|
||||
...options
|
||||
})
|
||||
;(unref(wrapRef) as HTMLImageElement).src = url
|
||||
emit('done', url)
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => renderText.value,
|
||||
(val) => {
|
||||
if (!val) return
|
||||
initQrcode()
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
const createLogoCode = (canvasRef: HTMLCanvasElement) => {
|
||||
const canvasWidth = canvasRef.width
|
||||
const logoOptions: QrcodeLogo = Object.assign(
|
||||
{
|
||||
logoSize: 0.15,
|
||||
bgColor: '#ffffff',
|
||||
borderSize: 0.05,
|
||||
crossOrigin: 'anonymous',
|
||||
borderRadius: 8,
|
||||
logoRadius: 0
|
||||
},
|
||||
isString(props.logo) ? {} : props.logo
|
||||
)
|
||||
const {
|
||||
logoSize = 0.15,
|
||||
bgColor = '#ffffff',
|
||||
borderSize = 0.05,
|
||||
crossOrigin = 'anonymous',
|
||||
borderRadius = 8,
|
||||
logoRadius = 0
|
||||
} = logoOptions
|
||||
const logoSrc = isString(props.logo) ? props.logo : props.logo.src
|
||||
const logoWidth = canvasWidth * logoSize
|
||||
const logoXY = (canvasWidth * (1 - logoSize)) / 2
|
||||
const logoBgWidth = canvasWidth * (logoSize + borderSize)
|
||||
const logoBgXY = (canvasWidth * (1 - logoSize - borderSize)) / 2
|
||||
|
||||
const ctx = canvasRef.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
// logo 底色
|
||||
canvasRoundRect(ctx)(logoBgXY, logoBgXY, logoBgWidth, logoBgWidth, borderRadius)
|
||||
ctx.fillStyle = bgColor
|
||||
ctx.fill()
|
||||
|
||||
// logo
|
||||
const image = new Image()
|
||||
if (crossOrigin || logoRadius) {
|
||||
image.setAttribute('crossOrigin', crossOrigin)
|
||||
}
|
||||
;(image as any).src = logoSrc
|
||||
|
||||
// 使用image绘制可以避免某些跨域情况
|
||||
const drawLogoWithImage = (image: HTMLImageElement) => {
|
||||
ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth)
|
||||
}
|
||||
|
||||
// 使用canvas绘制以获得更多的功能
|
||||
const drawLogoWithCanvas = (image: HTMLImageElement) => {
|
||||
const canvasImage = document.createElement('canvas')
|
||||
canvasImage.width = logoXY + logoWidth
|
||||
canvasImage.height = logoXY + logoWidth
|
||||
const imageCanvas = canvasImage.getContext('2d')
|
||||
if (!imageCanvas || !ctx) return
|
||||
imageCanvas.drawImage(image, logoXY, logoXY, logoWidth, logoWidth)
|
||||
|
||||
canvasRoundRect(ctx)(logoXY, logoXY, logoWidth, logoWidth, logoRadius)
|
||||
if (!ctx) return
|
||||
const fillStyle = ctx.createPattern(canvasImage, 'no-repeat')
|
||||
if (fillStyle) {
|
||||
ctx.fillStyle = fillStyle
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
|
||||
// 将 logo绘制到 canvas上
|
||||
return new Promise((resolve: any) => {
|
||||
image.onload = () => {
|
||||
logoRadius ? drawLogoWithCanvas(image) : drawLogoWithImage(image)
|
||||
resolve(canvasRef.toDataURL())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 得到原QrCode的大小,以便缩放得到正确的QrCode大小
|
||||
const getOriginWidth = async (content: string, options: QRCodeRenderersOptions) => {
|
||||
const _canvas = document.createElement('canvas')
|
||||
await toCanvas(_canvas, content, options)
|
||||
return _canvas.width
|
||||
}
|
||||
|
||||
// 对于内容少的QrCode,增大容错率
|
||||
const getErrorCorrectionLevel = (content: string) => {
|
||||
if (content.length > 36) {
|
||||
return 'M'
|
||||
} else if (content.length > 16) {
|
||||
return 'Q'
|
||||
} else {
|
||||
return 'H'
|
||||
}
|
||||
}
|
||||
|
||||
// copy来的方法,用于绘制圆角
|
||||
const canvasRoundRect = (ctx: CanvasRenderingContext2D) => {
|
||||
return (x: number, y: number, w: number, h: number, r: number) => {
|
||||
const minSize = Math.min(w, h)
|
||||
if (r > minSize / 2) {
|
||||
r = minSize / 2
|
||||
}
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(x + r, y)
|
||||
ctx.arcTo(x + w, y, x + w, y + h, r)
|
||||
ctx.arcTo(x + w, y + h, x, y + h, r)
|
||||
ctx.arcTo(x, y + h, x, y, r)
|
||||
ctx.arcTo(x, y, x + w, y, r)
|
||||
ctx.closePath()
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
|
||||
const clickCode = () => {
|
||||
emit('click')
|
||||
}
|
||||
|
||||
const disabledClick = () => {
|
||||
emit('disabled-click')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-loading="loading" :class="[prefixCls, 'relative inline-block']" :style="wrapStyle">
|
||||
<component :is="tag" ref="wrapRef" @click="clickCode" />
|
||||
<div
|
||||
v-if="disabled"
|
||||
:class="`${prefixCls}--disabled`"
|
||||
class="absolute top-0 left-0 flex w-full h-full items-center justify-center"
|
||||
@click="disabledClick"
|
||||
>
|
||||
<div class="absolute top-[50%] left-[50%] font-bold">
|
||||
<Icon icon="ep:refresh-right" :size="30" color="var(--el-color-primary)" />
|
||||
<div>{{ disabledText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-qrcode';
|
||||
|
||||
.@{prefix-cls} {
|
||||
&--disabled {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
|
||||
& > div {
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,9 +0,0 @@
|
|||
export interface QrcodeLogo {
|
||||
src?: string
|
||||
logoSize?: number
|
||||
bgColor?: string
|
||||
borderSize?: number
|
||||
crossOrigin?: string
|
||||
borderRadius?: number
|
||||
logoRadius?: number
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import type { App } from 'vue'
|
||||
import { setupPermissionDirective } from './permission/hasPermi'
|
||||
|
||||
/**
|
||||
* 导出指令:v-xxx
|
||||
* @methods hasPermi 按钮权限,用法: v-hasPermi
|
||||
*/
|
||||
export const setupPermission = (app: App<Element>) => {
|
||||
setupPermissionDirective(app)
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import type { App, Directive, DirectiveBinding } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useStorage } from '@/hooks/web/useStorage'
|
||||
import { intersection } from 'lodash-es'
|
||||
import { isArray } from '@/utils/is'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { getStorage } = useStorage()
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
// 全部权限
|
||||
const all_permission = ['*.*.*']
|
||||
const hasPermission = (value: string | string[]): boolean => {
|
||||
const permissions = getStorage(appStore.getUserInfo).permissions as string[]
|
||||
if (!value) {
|
||||
throw new Error(t('permission.hasPermission'))
|
||||
}
|
||||
if (!isArray(value)) {
|
||||
return permissions?.includes(value as string)
|
||||
}
|
||||
if (all_permission[0] === permissions[0]) {
|
||||
return true
|
||||
}
|
||||
return (intersection(value, permissions) as string[]).length > 0
|
||||
}
|
||||
function hasPermi(el: Element, binding: DirectiveBinding) {
|
||||
const value = binding.value
|
||||
|
||||
const flag = hasPermission(value)
|
||||
if (!flag) {
|
||||
el.parentNode?.removeChild(el)
|
||||
}
|
||||
}
|
||||
const mounted = (el: Element, binding: DirectiveBinding<any>) => {
|
||||
hasPermi(el, binding)
|
||||
}
|
||||
|
||||
const permiDirective: Directive = {
|
||||
mounted
|
||||
}
|
||||
|
||||
export const setupPermissionDirective = (app: App<Element>) => {
|
||||
app.directive('hasPermi', permiDirective)
|
||||
}
|
||||
|
||||
export default permiDirective
|
|
@ -25,9 +25,6 @@ import '@/plugins/animate.css'
|
|||
// 路由
|
||||
import { setupRouter } from './router'
|
||||
|
||||
// 权限
|
||||
import { setupPermission } from './directives'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
|
||||
import App from './App.vue'
|
||||
|
@ -48,8 +45,6 @@ const setupAll = async () => {
|
|||
|
||||
setupRouter(app)
|
||||
|
||||
setupPermission(app)
|
||||
|
||||
app.mount('#app')
|
||||
}
|
||||
|
||||
|
|
|
@ -56,292 +56,6 @@ export const constantRouterMap: AppRouteRecordRaw[] = [
|
|||
]
|
||||
|
||||
export const asyncRouterMap: AppRouteRecordRaw[] = [
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: Layout,
|
||||
redirect: '/dashboard/analysis',
|
||||
name: 'Dashboard',
|
||||
meta: {
|
||||
title: t('router.dashboard'),
|
||||
icon: 'ant-design:dashboard-filled',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'analysis',
|
||||
component: () => import('@/views/Dashboard/Analysis.vue'),
|
||||
name: 'Analysis',
|
||||
meta: {
|
||||
title: t('router.analysis'),
|
||||
noCache: true,
|
||||
affix: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'workplace',
|
||||
component: () => import('@/views/Dashboard/Workplace.vue'),
|
||||
name: 'Workplace',
|
||||
meta: {
|
||||
title: t('router.workplace'),
|
||||
noCache: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/external-link',
|
||||
component: Layout,
|
||||
meta: {},
|
||||
name: 'ExternalLink',
|
||||
children: [
|
||||
{
|
||||
path: 'https://element-plus-admin-doc.cn/',
|
||||
name: 'DocumentLink',
|
||||
meta: {
|
||||
title: t('router.document'),
|
||||
icon: 'clarity:document-solid'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/guide',
|
||||
component: Layout,
|
||||
name: 'Guide',
|
||||
meta: {},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/Guide/Guide.vue'),
|
||||
name: 'GuideDemo',
|
||||
meta: {
|
||||
title: t('router.guide'),
|
||||
icon: 'cib:telegram-plane'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/components',
|
||||
component: Layout,
|
||||
name: 'ComponentsDemo',
|
||||
meta: {
|
||||
title: t('router.component'),
|
||||
icon: 'bx:bxs-component',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'form',
|
||||
component: getParentLayout(),
|
||||
redirect: '/components/form/default-form',
|
||||
name: 'Form',
|
||||
meta: {
|
||||
title: t('router.form'),
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'default-form',
|
||||
component: () => import('@/views/Components/Form/DefaultForm.vue'),
|
||||
name: 'DefaultForm',
|
||||
meta: {
|
||||
title: t('router.defaultForm')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'use-form',
|
||||
component: () => import('@/views/Components/Form/UseFormDemo.vue'),
|
||||
name: 'UseForm',
|
||||
meta: {
|
||||
title: 'UseForm'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'table',
|
||||
component: getParentLayout(),
|
||||
redirect: '/components/table/default-table',
|
||||
name: 'TableDemo',
|
||||
meta: {
|
||||
title: t('router.table'),
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'default-table',
|
||||
component: () => import('@/views/Components/Table/DefaultTable.vue'),
|
||||
name: 'DefaultTable',
|
||||
meta: {
|
||||
title: t('router.defaultTable')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'use-table',
|
||||
component: () => import('@/views/Components/Table/UseTableDemo.vue'),
|
||||
name: 'UseTable',
|
||||
meta: {
|
||||
title: 'UseTable'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tree-table',
|
||||
component: () => import('@/views/Components/Table/TreeTable.vue'),
|
||||
name: 'TreeTable',
|
||||
meta: {
|
||||
title: t('router.treeTable')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'table-image-preview',
|
||||
component: () => import('@/views/Components/Table/TableImagePreview.vue'),
|
||||
name: 'TableImagePreview',
|
||||
meta: {
|
||||
title: t('router.PicturePreview')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'editor-demo',
|
||||
component: getParentLayout(),
|
||||
redirect: '/components/editor-demo/editor',
|
||||
name: 'EditorDemo',
|
||||
meta: {
|
||||
title: t('router.editor'),
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'editor',
|
||||
component: () => import('@/views/Components/Editor/Editor.vue'),
|
||||
name: 'Editor',
|
||||
meta: {
|
||||
title: t('router.richText')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
component: () => import('@/views/Components/Search.vue'),
|
||||
name: 'Search',
|
||||
meta: {
|
||||
title: t('router.search')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'descriptions',
|
||||
component: () => import('@/views/Components/Descriptions.vue'),
|
||||
name: 'Descriptions',
|
||||
meta: {
|
||||
title: t('router.descriptions')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'image-viewer',
|
||||
component: () => import('@/views/Components/ImageViewer.vue'),
|
||||
name: 'ImageViewer',
|
||||
meta: {
|
||||
title: t('router.imageViewer')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'dialog',
|
||||
component: () => import('@/views/Components/Dialog.vue'),
|
||||
name: 'Dialog',
|
||||
meta: {
|
||||
title: t('router.dialog')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'icon',
|
||||
component: () => import('@/views/Components/Icon.vue'),
|
||||
name: 'Icon',
|
||||
meta: {
|
||||
title: t('router.icon')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'echart',
|
||||
component: () => import('@/views/Components/Echart.vue'),
|
||||
name: 'Echart',
|
||||
meta: {
|
||||
title: t('router.echart')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'count-to',
|
||||
component: () => import('@/views/Components/CountTo.vue'),
|
||||
name: 'CountTo',
|
||||
meta: {
|
||||
title: t('router.countTo')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'qrcode',
|
||||
component: () => import('@/views/Components/Qrcode.vue'),
|
||||
name: 'Qrcode',
|
||||
meta: {
|
||||
title: t('router.qrcode')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'highlight',
|
||||
component: () => import('@/views/Components/Highlight.vue'),
|
||||
name: 'Highlight',
|
||||
meta: {
|
||||
title: t('router.highlight')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'infotip',
|
||||
component: () => import('@/views/Components/Infotip.vue'),
|
||||
name: 'Infotip',
|
||||
meta: {
|
||||
title: t('router.infotip')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'input-password',
|
||||
component: () => import('@/views/Components/InputPassword.vue'),
|
||||
name: 'InputPassword',
|
||||
meta: {
|
||||
title: t('router.inputPassword')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/hooks',
|
||||
component: Layout,
|
||||
redirect: '/hooks/useWatermark',
|
||||
name: 'Hooks',
|
||||
meta: {
|
||||
title: 'hooks',
|
||||
icon: 'ic:outline-webhook',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'useWatermark',
|
||||
component: () => import('@/views/hooks/useWatermark.vue'),
|
||||
name: 'UseWatermark',
|
||||
meta: {
|
||||
title: 'useWatermark'
|
||||
}
|
||||
}
|
||||
// {
|
||||
// path: 'useCrudSchemas',
|
||||
// component: () => import('@/views/hooks/useCrudSchemas.vue'),
|
||||
// name: 'UseCrudSchemas',
|
||||
// meta: {
|
||||
// title: 'useCrudSchemas'
|
||||
// }
|
||||
// }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level',
|
||||
component: Layout,
|
||||
|
@ -400,140 +114,6 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/example',
|
||||
component: Layout,
|
||||
redirect: '/example/example-dialog',
|
||||
name: 'Example',
|
||||
meta: {
|
||||
title: t('router.example'),
|
||||
icon: 'ep:management',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'example-dialog',
|
||||
component: () => import('@/views/Example/Dialog/ExampleDialog.vue'),
|
||||
name: 'ExampleDialog',
|
||||
meta: {
|
||||
title: t('router.exampleDialog')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-page',
|
||||
component: () => import('@/views/Example/Page/ExamplePage.vue'),
|
||||
name: 'ExamplePage',
|
||||
meta: {
|
||||
title: t('router.examplePage')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-add',
|
||||
component: () => import('@/views/Example/Page/ExampleAdd.vue'),
|
||||
name: 'ExampleAdd',
|
||||
meta: {
|
||||
title: t('router.exampleAdd'),
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
canTo: true,
|
||||
activeMenu: '/example/example-page'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-edit',
|
||||
component: () => import('@/views/Example/Page/ExampleEdit.vue'),
|
||||
name: 'ExampleEdit',
|
||||
meta: {
|
||||
title: t('router.exampleEdit'),
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
canTo: true,
|
||||
activeMenu: '/example/example-page'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-detail',
|
||||
component: () => import('@/views/Example/Page/ExampleDetail.vue'),
|
||||
name: 'ExampleDetail',
|
||||
meta: {
|
||||
title: t('router.exampleDetail'),
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
canTo: true,
|
||||
activeMenu: '/example/example-page'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/error',
|
||||
component: Layout,
|
||||
redirect: '/error/404',
|
||||
name: 'Error',
|
||||
meta: {
|
||||
title: t('router.errorPage'),
|
||||
icon: 'ci:error',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '404-demo',
|
||||
component: () => import('@/views/Error/404.vue'),
|
||||
name: '404Demo',
|
||||
meta: {
|
||||
title: '404'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '403-demo',
|
||||
component: () => import('@/views/Error/403.vue'),
|
||||
name: '403Demo',
|
||||
meta: {
|
||||
title: '403'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '500-demo',
|
||||
component: () => import('@/views/Error/500.vue'),
|
||||
name: '500Demo',
|
||||
meta: {
|
||||
title: '500'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/authorization',
|
||||
component: Layout,
|
||||
redirect: '/authorization/user',
|
||||
name: 'Authorization',
|
||||
meta: {
|
||||
title: t('router.authorization'),
|
||||
icon: 'eos-icons:role-binding',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
component: () => import('@/views/Authorization/User.vue'),
|
||||
name: 'User',
|
||||
meta: {
|
||||
title: t('router.user')
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
component: () => import('@/views/Authorization/Role.vue'),
|
||||
name: 'Role',
|
||||
meta: {
|
||||
title: t('router.role')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
// import { ContentWrap } from '@/components/ContentWrap'
|
||||
// import { useI18n } from '@/hooks/web/useI18n'
|
||||
// import { Table } from '@/components/Table'
|
||||
// import { getUserListApi } from '@/api/login'
|
||||
// import { UserType } from '@/api/login/types'
|
||||
// import { ref, h } from 'vue'
|
||||
// import { ElButton } from 'element-plus'
|
||||
// import { TableColumn, TableSlotDefault } from '@/types/table'
|
||||
|
||||
// interface Params {
|
||||
// pageIndex?: number
|
||||
// pageSize?: number
|
||||
// }
|
||||
|
||||
// const { t } = useI18n()
|
||||
|
||||
// const columns: TableColumn[] = [
|
||||
// {
|
||||
// field: 'index',
|
||||
// label: t('userDemo.index'),
|
||||
// type: 'index'
|
||||
// },
|
||||
// {
|
||||
// field: 'username',
|
||||
// label: t('userDemo.username')
|
||||
// },
|
||||
// {
|
||||
// field: 'password',
|
||||
// label: t('userDemo.password')
|
||||
// },
|
||||
// {
|
||||
// field: 'role',
|
||||
// label: t('userDemo.role')
|
||||
// },
|
||||
// {
|
||||
// field: 'remark',
|
||||
// label: t('userDemo.remark'),
|
||||
// formatter: (row: UserType) => {
|
||||
// return h(
|
||||
// 'span',
|
||||
// row.username === 'admin' ? t('userDemo.remarkMessage1') : t('userDemo.remarkMessage2')
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'action',
|
||||
// label: t('userDemo.action')
|
||||
// }
|
||||
// ]
|
||||
|
||||
// const loading = ref(true)
|
||||
|
||||
// let tableDataList = ref<UserType[]>([])
|
||||
|
||||
// const getTableList = async (params?: Params) => {
|
||||
// const res = await getUserListApi({
|
||||
// params: params || {
|
||||
// pageIndex: 1,
|
||||
// pageSize: 10
|
||||
// }
|
||||
// })
|
||||
// // .catch(() => {})
|
||||
// // .finally(() => {
|
||||
// // loading.value = false
|
||||
// // })
|
||||
// if (res) {
|
||||
// tableDataList.value = res.data.list
|
||||
// loading.value = false
|
||||
// }
|
||||
// }
|
||||
|
||||
// getTableList()
|
||||
|
||||
// const actionFn = (data: TableSlotDefault) => {
|
||||
// console.log(data)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>role</div>
|
||||
<!-- <ContentWrap :title="t('userDemo.title')" :message="t('userDemo.message')">
|
||||
<Table :columns="columns" :data="tableDataList" :loading="loading" :selection="false">
|
||||
<template #action="data">
|
||||
<ElButton type="primary" @click="actionFn(data as TableSlotDefault)">
|
||||
{{ t('tableDemo.action') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap> -->
|
||||
</template>
|
|
@ -1,355 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table } from '@/components/Table'
|
||||
import { ref, unref, nextTick, watch, reactive } from 'vue'
|
||||
import { ElButton, ElTree, ElInput, ElDivider } from 'element-plus'
|
||||
import { getDepartmentApi, getUserByIdApi, saveUserApi, deleteUserByIdApi } from '@/api/department'
|
||||
import type { DepartmentItem, DepartmentUserItem } from '@/api/department/types'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { Search } from '@/components/Search'
|
||||
import Write from './components/Write.vue'
|
||||
import Detail from './components/Detail.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { tableRegister, tableState, tableMethods } = useTable({
|
||||
fetchDataApi: async () => {
|
||||
const { pageSize, currentPage } = tableState
|
||||
const res = await getUserByIdApi({
|
||||
id: unref(currentNodeKey),
|
||||
pageIndex: unref(currentPage),
|
||||
pageSize: unref(pageSize),
|
||||
...unref(searchParams)
|
||||
})
|
||||
return {
|
||||
list: res.data.list || [],
|
||||
total: res.data.total || 0
|
||||
}
|
||||
},
|
||||
fetchDelApi: async () => {
|
||||
const res = await deleteUserByIdApi(unref(ids))
|
||||
return !!res
|
||||
}
|
||||
})
|
||||
const { total, loading, dataList, pageSize, currentPage } = tableState
|
||||
const { getList, getElTableExpose, delList } = tableMethods
|
||||
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
field: 'selection',
|
||||
search: {
|
||||
hidden: true
|
||||
},
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
},
|
||||
table: {
|
||||
type: 'selection'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'index',
|
||||
label: t('userDemo.index'),
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
search: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
},
|
||||
table: {
|
||||
type: 'index'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'username',
|
||||
label: t('userDemo.username')
|
||||
},
|
||||
{
|
||||
field: 'account',
|
||||
label: t('userDemo.account')
|
||||
},
|
||||
{
|
||||
field: 'department.id',
|
||||
label: t('userDemo.department'),
|
||||
detail: {
|
||||
slots: {
|
||||
default: (data: DepartmentUserItem) => {
|
||||
return <>{data.department.departmentName}</>
|
||||
}
|
||||
}
|
||||
},
|
||||
search: {
|
||||
hidden: true
|
||||
},
|
||||
form: {
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
nodeKey: 'id',
|
||||
props: {
|
||||
label: 'departmentName'
|
||||
}
|
||||
},
|
||||
optionApi: async () => {
|
||||
const res = await getDepartmentApi()
|
||||
return res.data.list
|
||||
}
|
||||
},
|
||||
table: {
|
||||
type: 'index'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'role',
|
||||
label: t('userDemo.role'),
|
||||
search: {
|
||||
hidden: true
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: t('userDemo.email'),
|
||||
form: {
|
||||
component: 'Input'
|
||||
},
|
||||
search: {
|
||||
hidden: true
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
label: t('userDemo.createTime'),
|
||||
form: {
|
||||
component: 'Input'
|
||||
},
|
||||
search: {
|
||||
hidden: true
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: t('userDemo.action'),
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
},
|
||||
search: {
|
||||
hidden: true
|
||||
},
|
||||
table: {
|
||||
width: 240,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
const row = data[0].row as DepartmentUserItem
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" onClick={() => action(row, 'edit')}>
|
||||
{t('exampleDemo.edit')}
|
||||
</ElButton>
|
||||
<ElButton type="success" onClick={() => action(row, 'detail')}>
|
||||
{t('exampleDemo.detail')}
|
||||
</ElButton>
|
||||
<ElButton type="danger" onClick={() => delData(row)}>
|
||||
{t('exampleDemo.del')}
|
||||
</ElButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
|
||||
const searchParams = ref({})
|
||||
const setSearchParams = (params: any) => {
|
||||
currentPage.value = 1
|
||||
searchParams.value = params
|
||||
getList()
|
||||
}
|
||||
|
||||
const treeEl = ref<typeof ElTree>()
|
||||
|
||||
const currentNodeKey = ref('')
|
||||
const departmentList = ref<DepartmentItem[]>([])
|
||||
const fetchDepartment = async () => {
|
||||
const res = await getDepartmentApi()
|
||||
departmentList.value = res.data.list
|
||||
currentNodeKey.value =
|
||||
(res.data.list[0] && res.data.list[0]?.children && res.data.list[0].children[0].id) || ''
|
||||
await nextTick()
|
||||
unref(treeEl)?.setCurrentKey(currentNodeKey.value)
|
||||
}
|
||||
fetchDepartment()
|
||||
|
||||
const currentDepartment = ref('')
|
||||
watch(
|
||||
() => currentDepartment.value,
|
||||
(val) => {
|
||||
unref(treeEl)!.filter(val)
|
||||
}
|
||||
)
|
||||
|
||||
const currentChange = (data: DepartmentItem) => {
|
||||
if (data.children) return
|
||||
currentNodeKey.value = data.id
|
||||
currentPage.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const filterNode = (value: string, data: DepartmentItem) => {
|
||||
if (!value) return true
|
||||
return data.departmentName.includes(value)
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
|
||||
const currentRow = ref<DepartmentUserItem>()
|
||||
const actionType = ref('')
|
||||
|
||||
const AddAction = () => {
|
||||
dialogTitle.value = t('exampleDemo.add')
|
||||
currentRow.value = undefined
|
||||
dialogVisible.value = true
|
||||
actionType.value = ''
|
||||
}
|
||||
|
||||
const delLoading = ref(false)
|
||||
const ids = ref<string[]>([])
|
||||
|
||||
const delData = async (row?: DepartmentUserItem) => {
|
||||
const elTableExpose = await getElTableExpose()
|
||||
ids.value = row
|
||||
? [row.id]
|
||||
: elTableExpose?.getSelectionRows().map((v: DepartmentUserItem) => v.id) || []
|
||||
delLoading.value = true
|
||||
|
||||
await delList(unref(ids).length).finally(() => {
|
||||
delLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const action = (row: DepartmentUserItem, type: string) => {
|
||||
dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
|
||||
actionType.value = type
|
||||
currentRow.value = { ...row, department: unref(treeEl)?.getCurrentNode() || {} }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const writeRef = ref<ComponentRef<typeof Write>>()
|
||||
|
||||
const saveLoading = ref(false)
|
||||
|
||||
const save = async () => {
|
||||
const write = unref(writeRef)
|
||||
const formData = await write?.submit()
|
||||
if (formData) {
|
||||
saveLoading.value = true
|
||||
try {
|
||||
const res = await saveUserApi(formData)
|
||||
if (res) {
|
||||
currentPage.value = 1
|
||||
getList()
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
} finally {
|
||||
saveLoading.value = false
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-100% h-100%">
|
||||
<ContentWrap class="flex-1">
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="flex-1">{{ t('userDemo.departmentList') }}</div>
|
||||
<ElInput
|
||||
v-model="currentDepartment"
|
||||
class="flex-[2]"
|
||||
:placeholder="t('userDemo.searchDepartment')"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<ElDivider />
|
||||
<ElTree
|
||||
ref="treeEl"
|
||||
:data="departmentList"
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:current-node-key="currentNodeKey"
|
||||
:props="{
|
||||
label: 'departmentName'
|
||||
}"
|
||||
:filter-node-method="filterNode"
|
||||
@current-change="currentChange"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<ContentWrap class="flex-[3] ml-20px">
|
||||
<Search
|
||||
:schema="allSchemas.searchSchema"
|
||||
@reset="setSearchParams"
|
||||
@search="setSearchParams"
|
||||
/>
|
||||
|
||||
<div class="mb-10px">
|
||||
<ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
|
||||
<ElButton :loading="delLoading" type="danger" @click="delData()">
|
||||
{{ t('exampleDemo.del') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
<Table
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:columns="allSchemas.tableColumns"
|
||||
:data="dataList"
|
||||
:loading="loading"
|
||||
@register="tableRegister"
|
||||
:pagination="{
|
||||
total
|
||||
}"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<Write
|
||||
v-if="actionType !== 'detail'"
|
||||
ref="writeRef"
|
||||
:form-schema="allSchemas.formSchema"
|
||||
:current-row="currentRow"
|
||||
/>
|
||||
|
||||
<Detail
|
||||
v-if="actionType === 'detail'"
|
||||
:detail-schema="allSchemas.detailSchema"
|
||||
:current-row="currentRow"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<ElButton
|
||||
v-if="actionType !== 'detail'"
|
||||
type="primary"
|
||||
:loading="saveLoading"
|
||||
@click="save"
|
||||
>
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
|
@ -1,20 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
import { DepartmentUserItem } from '@/api/department/types'
|
||||
import { Descriptions, DescriptionsSchema } from '@/components/Descriptions'
|
||||
|
||||
defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<DepartmentUserItem>,
|
||||
default: () => undefined
|
||||
},
|
||||
detailSchema: {
|
||||
type: Array as PropType<DescriptionsSchema[]>,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Descriptions :schema="detailSchema" :data="currentRow || {}" />
|
||||
</template>
|
|
@ -1,62 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { PropType, reactive, watch } from 'vue'
|
||||
import { DepartmentUserItem } from '@/api/department/types'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const props = defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<DepartmentUserItem>,
|
||||
default: () => undefined
|
||||
},
|
||||
formSchema: {
|
||||
type: Array as PropType<FormSchema[]>,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
username: [required()],
|
||||
account: [required()],
|
||||
role: [required()],
|
||||
email: [required()],
|
||||
createTime: [required()]
|
||||
})
|
||||
|
||||
const { formRegister, formMethods } = useForm()
|
||||
const { setValues, getFormData, getElFormExpose } = formMethods
|
||||
|
||||
const submit = async () => {
|
||||
const elForm = await getElFormExpose()
|
||||
const valid = await elForm?.validate().catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
if (valid) {
|
||||
const formData = getFormData()
|
||||
return formData
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.currentRow,
|
||||
(currentRow) => {
|
||||
if (!currentRow) return
|
||||
setValues(currentRow)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
defineExpose({
|
||||
submit
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form :rules="rules" @register="formRegister" :schema="formSchema" />
|
||||
</template>
|
|
@ -1,100 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { CountTo } from '@/components/CountTo'
|
||||
import { ElRow, ElCol, ElInputNumber, ElInput, ElButton } from 'element-plus'
|
||||
import { ref, unref } from 'vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const countRef = ref<ComponentRef<typeof CountTo>>()
|
||||
|
||||
const startVal = ref(0)
|
||||
|
||||
const endVal = ref(1314512)
|
||||
|
||||
const duration = ref(3000)
|
||||
|
||||
const decimals = ref(0)
|
||||
|
||||
const separator = ref(',')
|
||||
|
||||
const prefix = ref('¥ ')
|
||||
|
||||
const suffix = ref(' rmb')
|
||||
|
||||
const autoplay = ref(false)
|
||||
|
||||
const start = () => {
|
||||
unref(countRef)?.start()
|
||||
}
|
||||
|
||||
const pauseResume = () => {
|
||||
unref(countRef)?.pauseResume()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('countToDemo.countTo')" :message="t('countToDemo.countToDes')">
|
||||
<div class="text-center mb-40px">
|
||||
<CountTo
|
||||
ref="countRef"
|
||||
:start-val="startVal"
|
||||
:end-val="endVal"
|
||||
:duration="duration"
|
||||
:decimals="decimals"
|
||||
:separator="separator"
|
||||
:prefix="prefix"
|
||||
:suffix="suffix"
|
||||
:autoplay="autoplay"
|
||||
class="text-30px font-bold text-[var(--el-color-primary)]"
|
||||
/>
|
||||
</div>
|
||||
<ElRow :gutter="20" justify="space-between">
|
||||
<ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex mb-20px items-center">
|
||||
<span class="min-w-90px text-right">{{ t('countToDemo.startVal') }}:</span>
|
||||
<ElInputNumber v-model="startVal" :min="0" />
|
||||
</div>
|
||||
</ElCol>
|
||||
<ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex mb-20px items-center">
|
||||
<span class="min-w-90px text-right">{{ t('countToDemo.endVal') }}:</span>
|
||||
<ElInputNumber v-model="endVal" :min="1" />
|
||||
</div>
|
||||
</ElCol>
|
||||
<ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex mb-20px items-center">
|
||||
<span class="min-w-90px text-right">{{ t('countToDemo.duration') }}:</span>
|
||||
<ElInputNumber v-model="duration" :min="1000" />
|
||||
</div>
|
||||
</ElCol>
|
||||
<ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex mb-20px items-center">
|
||||
<span class="min-w-90px text-right">{{ t('countToDemo.separator') }}:</span>
|
||||
<ElInput v-model="separator" />
|
||||
</div>
|
||||
</ElCol>
|
||||
<ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex mb-20px items-center">
|
||||
<span class="min-w-90px text-right">{{ t('countToDemo.prefix') }}:</span>
|
||||
<ElInput v-model="prefix" />
|
||||
</div>
|
||||
</ElCol>
|
||||
<ElCol :xl="8" :lg="8" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex mb-20px items-center">
|
||||
<span class="min-w-90px text-right">{{ t('countToDemo.suffix') }}:</span>
|
||||
<ElInput v-model="suffix" />
|
||||
</div>
|
||||
</ElCol>
|
||||
<ElCol :span="24">
|
||||
<div class="text-center">
|
||||
<ElButton type="primary" @click="start">{{ t('countToDemo.start') }}</ElButton>
|
||||
<ElButton @click="pauseResume">
|
||||
{{ t('countToDemo.pause') }}/{{ t('countToDemo.resume') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,190 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { Descriptions } from '@/components/Descriptions'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { reactive } from 'vue'
|
||||
import { Form } from '@/components/Form'
|
||||
import { ElFormItem, ElInput, ElButton } from 'element-plus'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { DescriptionsSchema } from '@/components/Descriptions'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const data = reactive({
|
||||
username: 'chenkl',
|
||||
nickName: '梦似花落。',
|
||||
age: 26,
|
||||
phone: '13655971xxxx',
|
||||
email: '502431556@qq.com',
|
||||
addr: '这是一个很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的地址',
|
||||
sex: '男',
|
||||
certy: '3505831994xxxxxxxx'
|
||||
})
|
||||
|
||||
const schema = reactive<DescriptionsSchema[]>([
|
||||
{
|
||||
field: 'username',
|
||||
label: t('descriptionsDemo.username')
|
||||
},
|
||||
{
|
||||
field: 'nickName',
|
||||
label: t('descriptionsDemo.nickName')
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
label: t('descriptionsDemo.phone')
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: t('descriptionsDemo.email')
|
||||
},
|
||||
{
|
||||
field: 'addr',
|
||||
label: t('descriptionsDemo.addr')
|
||||
}
|
||||
])
|
||||
|
||||
const schema2 = reactive<DescriptionsSchema[]>([
|
||||
{
|
||||
field: 'username',
|
||||
label: t('descriptionsDemo.username'),
|
||||
slots: {
|
||||
label: (row) => {
|
||||
return <span class="is-required--item">{row.label}</span>
|
||||
},
|
||||
default: () => {
|
||||
return (
|
||||
<ElFormItem prop="username">
|
||||
<ElInput v-model={form.username} />
|
||||
</ElFormItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'nickName',
|
||||
label: t('descriptionsDemo.nickName'),
|
||||
slots: {
|
||||
label: (row) => {
|
||||
return <span class="is-required--item">{row.label}</span>
|
||||
},
|
||||
default: () => {
|
||||
return (
|
||||
<ElFormItem prop="nickName">
|
||||
<ElInput v-model={form.nickName} />
|
||||
</ElFormItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
label: t('descriptionsDemo.phone'),
|
||||
slots: {
|
||||
label: (row) => {
|
||||
return <span class="is-required--item">{row.label}</span>
|
||||
},
|
||||
default: () => {
|
||||
return (
|
||||
<ElFormItem prop="phone">
|
||||
<ElInput v-model={form.phone} />
|
||||
</ElFormItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: t('descriptionsDemo.email'),
|
||||
slots: {
|
||||
label: (row) => {
|
||||
return <span class="is-required--item">{row.label}</span>
|
||||
},
|
||||
default: () => {
|
||||
return (
|
||||
<ElFormItem prop="email">
|
||||
<ElInput v-model={form.email} />
|
||||
</ElFormItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'addr',
|
||||
label: t('descriptionsDemo.addr'),
|
||||
slots: {
|
||||
label: (row) => {
|
||||
return <span class="is-required--item">{row.label}</span>
|
||||
},
|
||||
default: () => {
|
||||
return (
|
||||
<ElFormItem prop="addr">
|
||||
<ElInput v-model={form.addr} />
|
||||
</ElFormItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const form = reactive({
|
||||
username: '',
|
||||
nickName: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
addr: ''
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
username: [required()],
|
||||
nickName: [required()],
|
||||
phone: [required()],
|
||||
email: [required()],
|
||||
addr: [required()]
|
||||
})
|
||||
|
||||
const { formRegister, formMethods } = useForm()
|
||||
const { getElFormExpose } = formMethods
|
||||
|
||||
const formValidation = async () => {
|
||||
const elFormExpose = await getElFormExpose()
|
||||
elFormExpose?.validate((isValid) => {
|
||||
console.log(isValid)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Descriptions
|
||||
:title="t('descriptionsDemo.descriptions')"
|
||||
:message="t('descriptionsDemo.descriptionsDes')"
|
||||
:data="data"
|
||||
:schema="schema"
|
||||
/>
|
||||
|
||||
<Form is-custom :model="form" :rules="rules" @register="formRegister">
|
||||
<Descriptions
|
||||
:title="t('descriptionsDemo.form')"
|
||||
:data="data"
|
||||
:schema="schema2"
|
||||
class="mt-20px"
|
||||
/>
|
||||
<div class="text-center mt-10px">
|
||||
<ElButton @click="formValidation"> {{ t('formDemo.formValidation') }} </ElButton>
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.is-required--item) {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
margin-right: 4px;
|
||||
color: var(--el-color-danger);
|
||||
content: '*';
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,131 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { getDictOneApi } from '@/api/common'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
const dialogVisible2 = ref(false)
|
||||
|
||||
const { formRegister, formMethods } = useForm()
|
||||
const { getElFormExpose } = formMethods
|
||||
|
||||
const schema = reactive<FormSchema[]>([
|
||||
{
|
||||
field: 'field1',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required()]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field2',
|
||||
label: t('formDemo.select'),
|
||||
component: 'Select',
|
||||
// componentProps: {
|
||||
// options: []
|
||||
// },
|
||||
optionApi: async () => {
|
||||
const res = await getDictOneApi()
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field3',
|
||||
label: t('formDemo.radio'),
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'option-1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option-2',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field4',
|
||||
label: t('formDemo.checkbox'),
|
||||
component: 'CheckboxGroup',
|
||||
value: [],
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'option-1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option-2',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field5',
|
||||
component: 'DatePicker',
|
||||
label: t('formDemo.datePicker'),
|
||||
componentProps: {
|
||||
type: 'date'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field6',
|
||||
component: 'TimeSelect',
|
||||
label: t('formDemo.timeSelect')
|
||||
}
|
||||
])
|
||||
|
||||
const formSubmit = async () => {
|
||||
const elFormExpose = await getElFormExpose()
|
||||
elFormExpose?.validate((valid) => {
|
||||
if (valid) {
|
||||
console.log('submit success')
|
||||
} else {
|
||||
console.log('submit fail')
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('dialogDemo.dialog')" :message="t('dialogDemo.dialogDes')">
|
||||
<ElButton type="primary" @click="dialogVisible = !dialogVisible">
|
||||
{{ t('dialogDemo.open') }}
|
||||
</ElButton>
|
||||
|
||||
<ElButton type="primary" @click="dialogVisible2 = !dialogVisible2">
|
||||
{{ t('dialogDemo.combineWithForm') }}
|
||||
</ElButton>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="t('dialogDemo.dialog')">
|
||||
<div v-for="v in 10000" :key="v">{{ v }}</div>
|
||||
<template #footer>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model="dialogVisible2" :title="t('dialogDemo.dialog')">
|
||||
<Form :schema="schema" @register="formRegister" />
|
||||
<template #footer>
|
||||
<ElButton type="primary" @click="formSubmit">{{ t('dialogDemo.submit') }}</ElButton>
|
||||
<ElButton @click="dialogVisible2 = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,36 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { pieOptions, barOptions, lineOptions, wordOptions } from '@/views/Dashboard/echarts-data'
|
||||
import { Echart } from '@/components/Echart'
|
||||
import { ElRow, ElCol, ElCard } from 'element-plus'
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('echartDemo.echart')" :message="t('echartDemo.echartDes')">
|
||||
<ElRow :gutter="20" justify="space-between">
|
||||
<ElCol :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<Echart :options="pieOptions" :height="300" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<Echart :options="barOptions" :height="300" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :span="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<Echart :options="lineOptions" :height="350" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :span="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<Echart :options="wordOptions as any" :height="300" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,32 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { Editor, EditorExpose } from '@/components/Editor'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { IDomEditor } from '@wangeditor/editor'
|
||||
import { ref, onMounted, unref } from 'vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const change = (editor: IDomEditor) => {
|
||||
console.log(editor.getHtml())
|
||||
}
|
||||
|
||||
const editorRef = ref<typeof Editor & EditorExpose>()
|
||||
|
||||
const defaultHtml = ref('')
|
||||
|
||||
onMounted(async () => {
|
||||
const editor = await unref(editorRef)?.getEditorRef()
|
||||
console.log(editor)
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
defaultHtml.value = '<p>hello <strong>world</strong></p>'
|
||||
}, 3000)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('richText.richText')" :message="t('richText.richTextDes')">
|
||||
<Editor v-model="defaultHtml" ref="editorRef" @change="change" />
|
||||
</ContentWrap>
|
||||
</template>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,316 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { reactive, unref, ref } from 'vue'
|
||||
import { ElButton, ElInput, FormItemProp, ComponentSize } from 'element-plus'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { getDictOneApi } from '@/api/common'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const schema = reactive<FormSchema[]>([
|
||||
{
|
||||
field: 'field1',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required()]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field2',
|
||||
label: t('formDemo.select'),
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'option1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option2',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
},
|
||||
formItemProps: {
|
||||
rules: [required()]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field3',
|
||||
label: t('formDemo.radio'),
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'option-1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option-2',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field4',
|
||||
label: t('formDemo.checkbox'),
|
||||
component: 'CheckboxGroup',
|
||||
value: [],
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'option-1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option-2',
|
||||
value: '2'
|
||||
},
|
||||
{
|
||||
label: 'option-3',
|
||||
value: '3'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field5',
|
||||
component: 'DatePicker',
|
||||
label: t('formDemo.datePicker'),
|
||||
componentProps: {
|
||||
type: 'date'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field6',
|
||||
component: 'TimeSelect',
|
||||
label: t('formDemo.timeSelect')
|
||||
}
|
||||
])
|
||||
|
||||
const { formRegister, formMethods } = useForm()
|
||||
const {
|
||||
setProps,
|
||||
delSchema,
|
||||
addSchema,
|
||||
setValues,
|
||||
setSchema,
|
||||
getComponentExpose,
|
||||
getFormItemExpose,
|
||||
getElFormExpose
|
||||
} = formMethods
|
||||
|
||||
const changeLabelWidth = (width: number | string) => {
|
||||
setProps({
|
||||
labelWidth: width
|
||||
})
|
||||
}
|
||||
|
||||
const changeSize = (size: ComponentSize) => {
|
||||
setProps({
|
||||
size
|
||||
})
|
||||
}
|
||||
|
||||
const changeDisabled = (bool: boolean) => {
|
||||
setProps({
|
||||
disabled: bool
|
||||
})
|
||||
}
|
||||
|
||||
const changeSchema = (del: boolean) => {
|
||||
if (del) {
|
||||
delSchema('field2')
|
||||
} else if (!del && schema[1].field !== 'field2') {
|
||||
addSchema(
|
||||
{
|
||||
field: 'field2',
|
||||
label: t('formDemo.select'),
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'option1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option2',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const setValue = async (reset: boolean) => {
|
||||
const elFormExpose = await getElFormExpose()
|
||||
if (reset) {
|
||||
elFormExpose?.resetFields()
|
||||
} else {
|
||||
setValues({
|
||||
field1: 'field1',
|
||||
field2: '2',
|
||||
field3: '2',
|
||||
field4: ['1', '3'],
|
||||
field5: '2022-01-27',
|
||||
field6: '17:00'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const index = ref(7)
|
||||
|
||||
const setLabel = () => {
|
||||
setSchema([
|
||||
{
|
||||
field: 'field2',
|
||||
path: 'label',
|
||||
value: `${t('formDemo.select')} ${index.value}`
|
||||
},
|
||||
{
|
||||
field: 'field2',
|
||||
path: 'componentProps.options',
|
||||
value: [
|
||||
{
|
||||
label: 'option-1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option-2',
|
||||
value: '2'
|
||||
},
|
||||
{
|
||||
label: 'option-3',
|
||||
value: '3'
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
index.value++
|
||||
}
|
||||
|
||||
const addItem = () => {
|
||||
if (unref(index) % 2 === 0) {
|
||||
addSchema({
|
||||
field: `field${unref(index)}`,
|
||||
label: `${t('formDemo.input')}${unref(index)}`,
|
||||
component: 'Input'
|
||||
})
|
||||
} else {
|
||||
addSchema(
|
||||
{
|
||||
field: `field${unref(index)}`,
|
||||
label: `${t('formDemo.input')}${unref(index)}`,
|
||||
component: 'Input'
|
||||
},
|
||||
unref(index)
|
||||
)
|
||||
}
|
||||
index.value++
|
||||
}
|
||||
|
||||
const formValidation = async () => {
|
||||
const elFormExpose = await getElFormExpose()
|
||||
elFormExpose?.validate((isValid) => {
|
||||
console.log(isValid)
|
||||
})
|
||||
}
|
||||
|
||||
const verifyReset = async () => {
|
||||
const elFormExpose = await getElFormExpose()
|
||||
elFormExpose?.resetFields()
|
||||
}
|
||||
|
||||
const getDictOne = async () => {
|
||||
const res = await getDictOneApi()
|
||||
if (res) {
|
||||
setSchema([
|
||||
{
|
||||
field: 'field2',
|
||||
path: 'componentProps.options',
|
||||
value: res.data
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
const inoutFocus = async () => {
|
||||
const inputEl: ComponentRef<typeof ElInput> = await getComponentExpose('field1')
|
||||
inputEl?.focus()
|
||||
}
|
||||
|
||||
const inoutValidation = async () => {
|
||||
const formItem = await getFormItemExpose('field1')
|
||||
const inputEl: ComponentRef<typeof ElInput> = await getComponentExpose('field1')
|
||||
inputEl?.focus()
|
||||
formItem?.validate('focus', (val: boolean) => {
|
||||
console.log(val)
|
||||
})
|
||||
}
|
||||
|
||||
const formValidate = (prop: FormItemProp, isValid: boolean, message: string) => {
|
||||
console.log(prop, isValid, message)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="`UseForm ${t('formDemo.operate')}`" style="margin-bottom: 20px">
|
||||
<ElButton @click="changeLabelWidth(150)">{{ t('formDemo.change') }} labelWidth</ElButton>
|
||||
<ElButton @click="changeLabelWidth('auto')">{{ t('formDemo.restore') }} labelWidth</ElButton>
|
||||
|
||||
<ElButton @click="changeSize('large')">{{ t('formDemo.change') }} size</ElButton>
|
||||
<ElButton @click="changeSize('default')">{{ t('formDemo.restore') }} size</ElButton>
|
||||
|
||||
<ElButton @click="changeDisabled(true)">{{ t('formDemo.disabled') }}</ElButton>
|
||||
<ElButton @click="changeDisabled(false)">{{ t('formDemo.disablement') }}</ElButton>
|
||||
|
||||
<ElButton @click="changeSchema(true)">
|
||||
{{ t('formDemo.delete') }} {{ t('formDemo.select') }}
|
||||
</ElButton>
|
||||
<ElButton @click="changeSchema(false)">
|
||||
{{ t('formDemo.add') }} {{ t('formDemo.select') }}
|
||||
</ElButton>
|
||||
|
||||
<ElButton @click="setValue(false)">{{ t('formDemo.setValue') }}</ElButton>
|
||||
<ElButton @click="setValue(true)">{{ t('formDemo.resetValue') }}</ElButton>
|
||||
|
||||
<ElButton @click="setLabel">
|
||||
{{ t('formDemo.set') }} {{ t('formDemo.select') }} label
|
||||
</ElButton>
|
||||
|
||||
<ElButton @click="addItem"> {{ t('formDemo.add') }} {{ t('formDemo.subitem') }} </ElButton>
|
||||
|
||||
<ElButton @click="formValidation"> {{ t('formDemo.formValidation') }} </ElButton>
|
||||
<ElButton @click="verifyReset"> {{ t('formDemo.verifyReset') }} </ElButton>
|
||||
|
||||
<ElButton @click="getDictOne">
|
||||
{{ `${t('formDemo.select')} ${t('searchDemo.dynamicOptions')}` }}
|
||||
</ElButton>
|
||||
|
||||
<ElButton @click="inoutFocus">
|
||||
{{ `${t('formDemo.input')} ${t('formDemo.focus')}` }}
|
||||
</ElButton>
|
||||
<ElButton @click="inoutValidation">
|
||||
{{ `${t('formDemo.input')} ${t('formDemo.formValidation')}` }}
|
||||
</ElButton>
|
||||
</ContentWrap>
|
||||
<ContentWrap :title="`UseForm ${t('formDemo.example')}`">
|
||||
<Form :schema="schema" @register="formRegister" @validate="formValidate" />
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,20 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Highlight } from '@/components/Highlight'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const keyClick = (key: string) => {
|
||||
ElMessage.info(key)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('highlightDemo.highlight')">
|
||||
<Highlight :keys="[t('highlightDemo.keys1'), t('highlightDemo.keys2')]" @click="keyClick">
|
||||
{{ t('highlightDemo.message') }}
|
||||
</Highlight>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,62 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Infotip } from '@/components/Infotip'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { useIcon } from '@/hooks/web/useIcon'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const keyClick = (key: string) => {
|
||||
if (key === t('iconDemo.accessAddress')) {
|
||||
window.open('https://iconify.design/')
|
||||
}
|
||||
}
|
||||
|
||||
const peoples = useIcon({ icon: 'svg-icon:peoples' })
|
||||
const money = useIcon({ icon: 'svg-icon:money' })
|
||||
const aim = useIcon({ icon: 'ep:aim' })
|
||||
const alarmClock = useIcon({ icon: 'ep:alarm-clock' })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Infotip
|
||||
:show-index="false"
|
||||
:title="`${t('iconDemo.recommendedUse')}${t('iconDemo.iconify')}`"
|
||||
:schema="[
|
||||
{
|
||||
label: t('iconDemo.recommendeDes'),
|
||||
keys: ['Iconify']
|
||||
},
|
||||
{
|
||||
label: t('iconDemo.accessAddress'),
|
||||
keys: [t('iconDemo.accessAddress')]
|
||||
}
|
||||
]"
|
||||
@click="keyClick"
|
||||
/>
|
||||
<ContentWrap :title="t('iconDemo.localIcon')">
|
||||
<div class="flex justify-between">
|
||||
<Icon icon="svg-icon:peoples" />
|
||||
<Icon icon="svg-icon:money" />
|
||||
<Icon icon="svg-icon:message" />
|
||||
<Icon icon="svg-icon:shopping" />
|
||||
</div>
|
||||
</ContentWrap>
|
||||
<ContentWrap :title="t('iconDemo.iconify')">
|
||||
<div class="flex justify-between">
|
||||
<Icon icon="ep:aim" />
|
||||
<Icon icon="ep:alarm-clock" />
|
||||
<Icon icon="ep:baseball" />
|
||||
<Icon icon="ep:chat-line-round" />
|
||||
</div>
|
||||
</ContentWrap>
|
||||
<ContentWrap title="useIcon">
|
||||
<div class="flex justify-between">
|
||||
<ElButton :icon="peoples">Button</ElButton>
|
||||
<ElButton :icon="money">Button</ElButton>
|
||||
<ElButton :icon="aim">Button</ElButton>
|
||||
<ElButton :icon="alarmClock">Button</ElButton>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,30 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const open = () => {
|
||||
createImageViewer({
|
||||
urlList: [
|
||||
'https://images6.alphacoders.com/657/thumbbig-657194.webp',
|
||||
'https://images3.alphacoders.com/677/thumbbig-677688.webp',
|
||||
'https://images4.alphacoders.com/200/thumbbig-200966.webp',
|
||||
'https://images5.alphacoders.com/657/thumbbig-657248.webp',
|
||||
'https://images3.alphacoders.com/679/thumbbig-679917.webp',
|
||||
'https://images3.alphacoders.com/737/thumbbig-73785.webp'
|
||||
]
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap
|
||||
:title="t('imageViewerDemo.imageViewer')"
|
||||
:message="t('imageViewerDemo.imageViewerDes')"
|
||||
>
|
||||
<ElButton type="primary" @click="open">{{ t('imageViewerDemo.open') }}</ElButton>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,33 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Infotip } from '@/components/Infotip'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const keyClick = (key: string) => {
|
||||
if (key === t('iconDemo.accessAddress')) {
|
||||
window.open('https://iconify.design/')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('infotipDemo.infotip')" :message="t('infotipDemo.infotipDes')">
|
||||
<Infotip
|
||||
:show-index="false"
|
||||
:title="`${t('iconDemo.recommendedUse')}${t('iconDemo.iconify')}`"
|
||||
:schema="[
|
||||
{
|
||||
label: t('iconDemo.recommendeDes'),
|
||||
keys: ['Iconify']
|
||||
},
|
||||
{
|
||||
label: t('iconDemo.accessAddress'),
|
||||
keys: [t('iconDemo.accessAddress')]
|
||||
}
|
||||
]"
|
||||
@click="keyClick"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,21 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { InputPassword } from '@/components/InputPassword'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const password = ref('')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap
|
||||
:title="t('inputPasswordDemo.title')"
|
||||
:message="t('inputPasswordDemo.inputPasswordDes')"
|
||||
>
|
||||
<InputPassword v-model="password" class="mb-20px" />
|
||||
<InputPassword v-model="password" strength />
|
||||
<InputPassword v-model="password" strength disabled class="mt-20px" />
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,108 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { Qrcode } from '@/components/Qrcode'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { computed, ref, unref } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { ElRow, ElCard, ElCol, ElMessage } from 'element-plus'
|
||||
// @ts-ignore
|
||||
import logoImg from '@/assets/imgs/logo.png'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const title = computed(() => appStore.getTitle)
|
||||
|
||||
const asyncTitle = ref('')
|
||||
|
||||
setTimeout(() => {
|
||||
asyncTitle.value = unref(title)
|
||||
}, 3000)
|
||||
|
||||
const codeClick = () => {
|
||||
ElMessage.info(t('qrcodeDemo.click'))
|
||||
}
|
||||
|
||||
const disabledClick = () => {
|
||||
ElMessage.info(t('qrcodeDemo.invalid'))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('qrcodeDemo.qrcode')" :message="t('qrcodeDemo.qrcodeDes')">
|
||||
<ElRow :gutter="20" justify="space-between">
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.basicUsage') }}</div>
|
||||
<Qrcode :text="title" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.imgTag') }}</div>
|
||||
<Qrcode :text="title" tag="img" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.style') }}</div>
|
||||
<Qrcode
|
||||
:text="title"
|
||||
:options="{
|
||||
color: {
|
||||
dark: '#55D187',
|
||||
light: '#2d8cf0'
|
||||
}
|
||||
}"
|
||||
/>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.click') }}</div>
|
||||
<Qrcode :text="title" @click="codeClick" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.asynchronousContent') }}</div>
|
||||
<Qrcode :text="asyncTitle" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.invalid') }}</div>
|
||||
<Qrcode :text="title" disabled @disabled-click="disabledClick" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.logoConfig') }}</div>
|
||||
<Qrcode :text="title" :logo="logoImg" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.logoStyle') }}</div>
|
||||
<Qrcode
|
||||
:text="title"
|
||||
:logo="{
|
||||
src: logoImg,
|
||||
logoSize: 0.2,
|
||||
borderSize: 0.05,
|
||||
borderRadius: 50,
|
||||
bgColor: 'blue'
|
||||
}"
|
||||
/>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.size') }}</div>
|
||||
<Qrcode :text="title" :width="100" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,272 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Search } from '@/components/Search'
|
||||
import { reactive, ref, unref } from 'vue'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { getDictOneApi } from '@/api/common'
|
||||
import { FormSchema } from '@/components/Form'
|
||||
import { useSearch } from '@/hooks/web/useSearch'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { searchRegister, searchMethods } = useSearch()
|
||||
const { setSchema, setProps, setValues } = searchMethods
|
||||
|
||||
const schema = reactive<FormSchema[]>([
|
||||
{
|
||||
field: 'field1',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field2',
|
||||
label: t('formDemo.select'),
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'option1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option2',
|
||||
value: '2'
|
||||
}
|
||||
],
|
||||
on: {
|
||||
change: (value: string) => {
|
||||
console.log(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field3',
|
||||
label: t('formDemo.radio'),
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'option-1',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'option-2',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field5',
|
||||
component: 'DatePicker',
|
||||
label: t('formDemo.datePicker'),
|
||||
componentProps: {
|
||||
type: 'date'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field6',
|
||||
component: 'TimeSelect',
|
||||
label: t('formDemo.timeSelect')
|
||||
},
|
||||
{
|
||||
field: 'field8',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field9',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field10',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field11',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field12',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field13',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field14',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field15',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field16',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field17',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'field18',
|
||||
label: t('formDemo.input'),
|
||||
component: 'Input'
|
||||
}
|
||||
])
|
||||
|
||||
const isGrid = ref(false)
|
||||
|
||||
const changeGrid = (grid: boolean) => {
|
||||
setProps({
|
||||
isCol: grid
|
||||
})
|
||||
// isGrid.value = grid
|
||||
}
|
||||
|
||||
const layout = ref('inline')
|
||||
|
||||
const changeLayout = () => {
|
||||
layout.value = unref(layout) === 'inline' ? 'bottom' : 'inline'
|
||||
}
|
||||
|
||||
const buttonPosition = ref('left')
|
||||
|
||||
const changePosition = (position: string) => {
|
||||
layout.value = 'bottom'
|
||||
buttonPosition.value = position
|
||||
}
|
||||
|
||||
const getDictOne = async () => {
|
||||
const res = await getDictOneApi()
|
||||
if (res) {
|
||||
setSchema([
|
||||
{
|
||||
field: 'field2',
|
||||
path: 'componentProps.options',
|
||||
value: res.data
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = (data: any) => {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
const delRadio = () => {
|
||||
setSchema([
|
||||
{
|
||||
field: 'field3',
|
||||
path: 'remove',
|
||||
value: true
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
const restoreRadio = () => {
|
||||
setSchema([
|
||||
{
|
||||
field: 'field3',
|
||||
path: 'remove',
|
||||
value: false
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
const setValue = () => {
|
||||
setValues({
|
||||
field1: 'Joy'
|
||||
})
|
||||
}
|
||||
|
||||
const searchLoading = ref(false)
|
||||
const changeSearchLoading = () => {
|
||||
searchLoading.value = true
|
||||
setTimeout(() => {
|
||||
searchLoading.value = false
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const resetLoading = ref(false)
|
||||
const changeResetLoading = () => {
|
||||
resetLoading.value = true
|
||||
setTimeout(() => {
|
||||
resetLoading.value = false
|
||||
}, 2000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap
|
||||
:title="`${t('searchDemo.search')} ${t('searchDemo.operate')}`"
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
<ElButton @click="changeGrid(true)">{{ t('searchDemo.grid') }}</ElButton>
|
||||
<ElButton @click="changeGrid(false)">
|
||||
{{ t('searchDemo.restore') }} {{ t('searchDemo.grid') }}
|
||||
</ElButton>
|
||||
|
||||
<ElButton @click="changeLayout">
|
||||
{{ t('searchDemo.button') }} {{ t('searchDemo.position') }}
|
||||
</ElButton>
|
||||
|
||||
<ElButton @click="changePosition('left')">
|
||||
{{ t('searchDemo.bottom') }} {{ t('searchDemo.position') }}-{{ t('searchDemo.left') }}
|
||||
</ElButton>
|
||||
<ElButton @click="changePosition('center')">
|
||||
{{ t('searchDemo.bottom') }} {{ t('searchDemo.position') }}-{{ t('searchDemo.center') }}
|
||||
</ElButton>
|
||||
<ElButton @click="changePosition('right')">
|
||||
{{ t('searchDemo.bottom') }} {{ t('searchDemo.position') }}-{{ t('searchDemo.right') }}
|
||||
</ElButton>
|
||||
<ElButton @click="getDictOne">
|
||||
{{ t('formDemo.select') }} {{ t('searchDemo.dynamicOptions') }}
|
||||
</ElButton>
|
||||
<ElButton @click="delRadio">{{ t('searchDemo.deleteRadio') }}</ElButton>
|
||||
<ElButton @click="restoreRadio">{{ t('searchDemo.restoreRadio') }}</ElButton>
|
||||
<ElButton @click="setValue">{{ t('formDemo.setValue') }}</ElButton>
|
||||
|
||||
<ElButton @click="changeSearchLoading">
|
||||
{{ t('searchDemo.search') }} {{ t('searchDemo.loading') }}
|
||||
</ElButton>
|
||||
<ElButton @click="changeResetLoading">
|
||||
{{ t('searchDemo.reset') }} {{ t('searchDemo.loading') }}
|
||||
</ElButton>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap :title="t('searchDemo.search')" :message="t('searchDemo.searchDes')">
|
||||
<Search
|
||||
:schema="schema"
|
||||
:is-col="isGrid"
|
||||
:layout="layout"
|
||||
:button-position="buttonPosition"
|
||||
:search-loading="searchLoading"
|
||||
:reset-loading="resetLoading"
|
||||
show-expand
|
||||
expand-field="field6"
|
||||
@search="handleSearch"
|
||||
@reset="handleSearch"
|
||||
@register="searchRegister"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,104 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn, TableSlotDefault } from '@/components/Table'
|
||||
import { getTableListApi } from '@/api/table'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { ref, h } from 'vue'
|
||||
import { ElTag, ElButton } from 'element-plus'
|
||||
|
||||
interface Params {
|
||||
pageIndex?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
field: 'title',
|
||||
label: t('tableDemo.title')
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('tableDemo.author')
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('tableDemo.displayTime'),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('tableDemo.importance'),
|
||||
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
|
||||
return h(
|
||||
ElTag,
|
||||
{
|
||||
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
|
||||
},
|
||||
() =>
|
||||
cellValue === 1
|
||||
? t('tableDemo.important')
|
||||
: cellValue === 2
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('tableDemo.pageviews')
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: t('tableDemo.action'),
|
||||
slots: {
|
||||
default: (data) => {
|
||||
return (
|
||||
<ElButton type="primary" onClick={() => actionFn(data)}>
|
||||
{t('tableDemo.action')}
|
||||
</ElButton>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
let tableDataList = ref<TableData[]>([])
|
||||
|
||||
const getTableList = async (params?: Params) => {
|
||||
const res = await getTableListApi(
|
||||
params || {
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
)
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
if (res) {
|
||||
tableDataList.value = res.data.list
|
||||
}
|
||||
}
|
||||
|
||||
getTableList()
|
||||
|
||||
const actionFn = (data: TableSlotDefault) => {
|
||||
console.log(data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('tableDemo.table')" :message="t('tableDemo.tableDes')">
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data="tableDataList"
|
||||
:loading="loading"
|
||||
:defaultSort="{ prop: 'display_time', order: 'descending' }"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,82 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { getTableListApi } from '@/api/table'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { ref } from 'vue'
|
||||
import { ElTag } from 'element-plus'
|
||||
|
||||
interface Params {
|
||||
pageIndex?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
field: 'title',
|
||||
label: t('tableDemo.title')
|
||||
},
|
||||
{
|
||||
field: 'image_uri',
|
||||
label: t('tableDemo.preview')
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('tableDemo.author')
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('tableDemo.displayTime')
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('tableDemo.importance'),
|
||||
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
|
||||
return (
|
||||
<ElTag type={cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'}>
|
||||
{cellValue === 1
|
||||
? t('tableDemo.important')
|
||||
: cellValue === 2
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')}
|
||||
</ElTag>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('tableDemo.pageviews')
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
let tableDataList = ref<TableData[]>([])
|
||||
|
||||
const getTableList = async (params?: Params) => {
|
||||
const res = await getTableListApi(
|
||||
params || {
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
)
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
if (res) {
|
||||
tableDataList.value = res.data.list
|
||||
}
|
||||
}
|
||||
|
||||
getTableList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('router.PicturePreview')">
|
||||
<Table :columns="columns" :data="tableDataList" :loading="loading" :preview="['image_uri']" />
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,116 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn, TableSlotDefault } from '@/components/Table'
|
||||
import { getTreeTableListApi } from '@/api/table'
|
||||
import { reactive, unref } from 'vue'
|
||||
import { ElTag, ElButton } from 'element-plus'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
|
||||
const { tableRegister, tableState } = useTable({
|
||||
fetchDataApi: async () => {
|
||||
const { currentPage, pageSize } = tableState
|
||||
const res = await getTreeTableListApi({
|
||||
pageIndex: unref(currentPage),
|
||||
pageSize: unref(pageSize)
|
||||
})
|
||||
return {
|
||||
list: res.data.list,
|
||||
total: res.data.total
|
||||
}
|
||||
}
|
||||
})
|
||||
const { loading, dataList, total, currentPage, pageSize } = tableState
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const columns = reactive<TableColumn[]>([
|
||||
{
|
||||
field: 'selection',
|
||||
type: 'selection'
|
||||
},
|
||||
{
|
||||
field: 'index',
|
||||
label: t('tableDemo.index'),
|
||||
type: 'index'
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: t('tableDemo.header'),
|
||||
children: [
|
||||
{
|
||||
field: 'title',
|
||||
label: t('tableDemo.title')
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('tableDemo.author')
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('tableDemo.displayTime')
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('tableDemo.importance'),
|
||||
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
|
||||
return (
|
||||
<ElTag type={cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'}>
|
||||
{cellValue === 1
|
||||
? t('tableDemo.important')
|
||||
: cellValue === 2
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')}
|
||||
</ElTag>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('tableDemo.pageviews')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: t('tableDemo.action'),
|
||||
slots: {
|
||||
default: (data) => {
|
||||
return (
|
||||
<ElButton type="primary" onClick={() => actionFn(data)}>
|
||||
{t('tableDemo.action')}
|
||||
</ElButton>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const actionFn = (data: TableSlotDefault) => {
|
||||
console.log(data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="`${t('router.treeTable')} ${t('tableDemo.example')}`">
|
||||
<Table
|
||||
v-model:pageSize="pageSize"
|
||||
v-model:currentPage="currentPage"
|
||||
:columns="columns"
|
||||
:data="dataList"
|
||||
row-key="id"
|
||||
:loading="loading"
|
||||
sortable
|
||||
:pagination="{
|
||||
total: total
|
||||
}"
|
||||
@register="tableRegister"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,275 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table, TableColumn, TableSlotDefault } from '@/components/Table'
|
||||
import { getTableListApi } from '@/api/table'
|
||||
import { ref, reactive, unref } from 'vue'
|
||||
import { ElTag, ElButton } from 'element-plus'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
|
||||
const { tableRegister, tableMethods, tableState } = useTable({
|
||||
fetchDataApi: async () => {
|
||||
const { currentPage, pageSize } = tableState
|
||||
const res = await getTableListApi({
|
||||
pageIndex: unref(currentPage),
|
||||
pageSize: unref(pageSize)
|
||||
})
|
||||
return {
|
||||
list: res.data.list,
|
||||
total: res.data.total
|
||||
}
|
||||
}
|
||||
})
|
||||
const { loading, dataList, total, currentPage, pageSize } = tableState
|
||||
const { setProps, setColumn, getElTableExpose, addColumn, delColumn, refresh } = tableMethods
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const columns = reactive<TableColumn[]>([
|
||||
{
|
||||
field: 'expand',
|
||||
type: 'expand',
|
||||
slots: {
|
||||
default: (data: TableSlotDefault[]) => {
|
||||
const { row } = data[0]
|
||||
return (
|
||||
<div class="ml-30px">
|
||||
<div>
|
||||
{t('tableDemo.title')}:{row.title}
|
||||
</div>
|
||||
<div>
|
||||
{t('tableDemo.author')}:{row.author}
|
||||
</div>
|
||||
<div>
|
||||
{t('tableDemo.displayTime')}:{row.display_time}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'selection',
|
||||
type: 'selection'
|
||||
},
|
||||
{
|
||||
field: 'index',
|
||||
label: t('tableDemo.index'),
|
||||
type: 'index'
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: t('tableDemo.header'),
|
||||
children: [
|
||||
{
|
||||
field: 'title',
|
||||
label: t('tableDemo.title')
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('tableDemo.author')
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('tableDemo.displayTime')
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('tableDemo.importance'),
|
||||
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
|
||||
return (
|
||||
<ElTag type={cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'}>
|
||||
{cellValue === 1
|
||||
? t('tableDemo.important')
|
||||
: cellValue === 2
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')}
|
||||
</ElTag>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('tableDemo.pageviews')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: t('tableDemo.action'),
|
||||
slots: {
|
||||
default: (data) => {
|
||||
return (
|
||||
<ElButton type="primary" onClick={() => actionFn(data)}>
|
||||
{t('tableDemo.action')}
|
||||
</ElButton>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
const actionFn = (data: TableSlotDefault) => {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
const canShowPagination = ref(true)
|
||||
const showPagination = (show: boolean) => {
|
||||
canShowPagination.value = show
|
||||
}
|
||||
|
||||
const reserveIndex = (custom: boolean) => {
|
||||
setProps({
|
||||
reserveIndex: custom
|
||||
})
|
||||
}
|
||||
|
||||
const showSelections = (show: boolean) => {
|
||||
setColumn([
|
||||
{
|
||||
field: 'selection',
|
||||
path: 'hidden',
|
||||
value: !show
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
const index = ref(1)
|
||||
|
||||
const changeTitle = () => {
|
||||
setColumn([
|
||||
{
|
||||
field: 'title',
|
||||
path: 'label',
|
||||
value: `${t('tableDemo.title')}${unref(index)}`
|
||||
}
|
||||
])
|
||||
index.value++
|
||||
}
|
||||
|
||||
const showExpandedRows = (show: boolean) => {
|
||||
setColumn([
|
||||
{
|
||||
field: 'expand',
|
||||
path: 'hidden',
|
||||
value: !show
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
const selectAllNone = async () => {
|
||||
const elTableRef = await getElTableExpose()
|
||||
elTableRef?.toggleAllSelection()
|
||||
}
|
||||
|
||||
const showAction = ref(true)
|
||||
const delOrAddAction = () => {
|
||||
if (unref(showAction)) {
|
||||
delColumn('action')
|
||||
showAction.value = false
|
||||
} else {
|
||||
addColumn({
|
||||
field: 'action',
|
||||
label: t('tableDemo.action'),
|
||||
slots: {
|
||||
default: (data) => {
|
||||
return (
|
||||
<ElButton type="primary" onClick={() => actionFn(data)}>
|
||||
{t('tableDemo.action')}
|
||||
</ElButton>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
showAction.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const showStripe = ref(false)
|
||||
const showOrHiddenStripe = () => {
|
||||
setProps({
|
||||
stripe: !unref(showStripe)
|
||||
})
|
||||
showStripe.value = !unref(showStripe)
|
||||
}
|
||||
|
||||
const height = ref<string | number>('auto')
|
||||
const fixedHeaderOrAuto = () => {
|
||||
if (unref(height) === 'auto') {
|
||||
setProps({
|
||||
height: 300
|
||||
})
|
||||
height.value = 300
|
||||
} else {
|
||||
setProps({
|
||||
height: 'auto'
|
||||
})
|
||||
height.value = 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
const getSelections = async () => {
|
||||
const elTableRef = await getElTableExpose()
|
||||
const selections = elTableRef?.getSelectionRows()
|
||||
console.log(selections)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="`UseTable ${t('tableDemo.operate')}`" style="margin-bottom: 20px">
|
||||
<ElButton @click="showPagination(true)">
|
||||
{{ t('tableDemo.show') }} {{ t('tableDemo.pagination') }}
|
||||
</ElButton>
|
||||
<ElButton @click="showPagination(false)">
|
||||
{{ t('tableDemo.hidden') }} {{ t('tableDemo.pagination') }}
|
||||
</ElButton>
|
||||
|
||||
<ElButton @click="reserveIndex(true)">{{ t('tableDemo.reserveIndex') }}</ElButton>
|
||||
<ElButton @click="reserveIndex(false)">{{ t('tableDemo.restoreIndex') }}</ElButton>
|
||||
|
||||
<ElButton @click="showSelections(true)">{{ t('tableDemo.showSelections') }}</ElButton>
|
||||
<ElButton @click="showSelections(false)">{{ t('tableDemo.hiddenSelections') }}</ElButton>
|
||||
|
||||
<ElButton @click="changeTitle">{{ t('tableDemo.changeTitle') }}</ElButton>
|
||||
|
||||
<ElButton @click="showExpandedRows(true)">{{ t('tableDemo.showExpandedRows') }}</ElButton>
|
||||
<ElButton @click="showExpandedRows(false)">{{ t('tableDemo.hiddenExpandedRows') }}</ElButton>
|
||||
|
||||
<ElButton @click="selectAllNone">{{ t('tableDemo.selectAllNone') }}</ElButton>
|
||||
|
||||
<ElButton @click="delOrAddAction">{{ t('tableDemo.delOrAddAction') }}</ElButton>
|
||||
|
||||
<ElButton @click="showOrHiddenStripe">{{ t('tableDemo.showOrHiddenStripe') }}</ElButton>
|
||||
|
||||
<ElButton @click="fixedHeaderOrAuto">{{ t('tableDemo.fixedHeaderOrAuto') }}</ElButton>
|
||||
|
||||
<ElButton @click="getSelections">{{ t('tableDemo.getSelections') }}</ElButton>
|
||||
|
||||
<!-- <ElButton @click="showOrHiddenSortable">{{ t('tableDemo.showOrHiddenSortable') }}</ElButton> -->
|
||||
</ContentWrap>
|
||||
<ContentWrap :title="`UseTable ${t('tableDemo.example')}`">
|
||||
<Table
|
||||
v-model:pageSize="pageSize"
|
||||
v-model:currentPage="currentPage"
|
||||
showAction
|
||||
:columns="columns"
|
||||
:data="dataList"
|
||||
:loading="loading"
|
||||
:pagination="
|
||||
canShowPagination
|
||||
? {
|
||||
total: total
|
||||
}
|
||||
: undefined
|
||||
"
|
||||
@register="tableRegister"
|
||||
@refresh="refresh"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,127 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import PanelGroup from './components/PanelGroup.vue'
|
||||
import { ElRow, ElCol, ElCard, ElSkeleton } from 'element-plus'
|
||||
import { Echart } from '@/components/Echart'
|
||||
import { pieOptions, barOptions, lineOptions } from './echarts-data'
|
||||
import { ref, reactive } from 'vue'
|
||||
import {
|
||||
getUserAccessSourceApi,
|
||||
getWeeklyUserActivityApi,
|
||||
getMonthlySalesApi
|
||||
} from '@/api/dashboard/analysis'
|
||||
import { set } from 'lodash-es'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
|
||||
|
||||
// 用户来源
|
||||
const getUserAccessSource = async () => {
|
||||
const res = await getUserAccessSourceApi().catch(() => {})
|
||||
if (res) {
|
||||
set(
|
||||
pieOptionsData,
|
||||
'legend.data',
|
||||
res.data.map((v) => t(v.name))
|
||||
)
|
||||
pieOptionsData!.series![0].data = res.data.map((v) => {
|
||||
return {
|
||||
name: t(v.name),
|
||||
value: v.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
|
||||
|
||||
// 周活跃量
|
||||
const getWeeklyUserActivity = async () => {
|
||||
const res = await getWeeklyUserActivityApi().catch(() => {})
|
||||
if (res) {
|
||||
set(
|
||||
barOptionsData,
|
||||
'xAxis.data',
|
||||
res.data.map((v) => t(v.name))
|
||||
)
|
||||
set(barOptionsData, 'series', [
|
||||
{
|
||||
name: t('analysis.activeQuantity'),
|
||||
data: res.data.map((v) => v.value),
|
||||
type: 'bar'
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
const lineOptionsData = reactive<EChartsOption>(lineOptions) as EChartsOption
|
||||
|
||||
// 每月销售总额
|
||||
const getMonthlySales = async () => {
|
||||
const res = await getMonthlySalesApi().catch(() => {})
|
||||
if (res) {
|
||||
set(
|
||||
lineOptionsData,
|
||||
'xAxis.data',
|
||||
res.data.map((v) => t(v.name))
|
||||
)
|
||||
set(lineOptionsData, 'series', [
|
||||
{
|
||||
name: t('analysis.estimate'),
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
data: res.data.map((v) => v.estimate),
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'cubicInOut'
|
||||
},
|
||||
{
|
||||
name: t('analysis.actual'),
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
itemStyle: {},
|
||||
data: res.data.map((v) => v.actual),
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'quadraticOut'
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
const getAllApi = async () => {
|
||||
await Promise.all([getUserAccessSource(), getWeeklyUserActivity(), getMonthlySales()])
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
getAllApi()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PanelGroup />
|
||||
<ElRow :gutter="20" justify="space-between">
|
||||
<ElCol :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<Echart :options="pieOptionsData" :height="300" />
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<Echart :options="barOptionsData" :height="300" />
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :span="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="4">
|
||||
<Echart :options="lineOptionsData" :height="350" />
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
|
@ -1,293 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { useTimeAgo } from '@/hooks/web/useTimeAgo'
|
||||
import { ElRow, ElCol, ElSkeleton, ElCard, ElDivider, ElLink } from 'element-plus'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { CountTo } from '@/components/CountTo'
|
||||
import { formatTime } from '@/utils'
|
||||
import { Echart } from '@/components/Echart'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { radarOption } from './echarts-data'
|
||||
import { Highlight } from '@/components/Highlight'
|
||||
import {
|
||||
getCountApi,
|
||||
getProjectApi,
|
||||
getDynamicApi,
|
||||
getTeamApi,
|
||||
getRadarApi
|
||||
} from '@/api/dashboard/workplace'
|
||||
import type { WorkplaceTotal, Project, Dynamic, Team } from '@/api/dashboard/workplace/types'
|
||||
import { set } from 'lodash-es'
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
// 获取统计数
|
||||
let totalSate = reactive<WorkplaceTotal>({
|
||||
project: 0,
|
||||
access: 0,
|
||||
todo: 0
|
||||
})
|
||||
|
||||
const getCount = async () => {
|
||||
const res = await getCountApi().catch(() => {})
|
||||
if (res) {
|
||||
totalSate = Object.assign(totalSate, res.data)
|
||||
}
|
||||
}
|
||||
|
||||
let projects = reactive<Project[]>([])
|
||||
|
||||
// 获取项目数
|
||||
const getProject = async () => {
|
||||
const res = await getProjectApi().catch(() => {})
|
||||
if (res) {
|
||||
projects = Object.assign(projects, res.data)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取动态
|
||||
let dynamics = reactive<Dynamic[]>([])
|
||||
|
||||
const getDynamic = async () => {
|
||||
const res = await getDynamicApi().catch(() => {})
|
||||
if (res) {
|
||||
dynamics = Object.assign(dynamics, res.data)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取团队
|
||||
let team = reactive<Team[]>([])
|
||||
|
||||
const getTeam = async () => {
|
||||
const res = await getTeamApi().catch(() => {})
|
||||
if (res) {
|
||||
team = Object.assign(team, res.data)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取指数
|
||||
let radarOptionData = reactive<EChartsOption>(radarOption) as EChartsOption
|
||||
|
||||
const getRadar = async () => {
|
||||
const res = await getRadarApi().catch(() => {})
|
||||
if (res) {
|
||||
set(
|
||||
radarOptionData,
|
||||
'radar.indicator',
|
||||
res.data.map((v) => {
|
||||
return {
|
||||
name: t(v.name),
|
||||
max: v.max
|
||||
}
|
||||
})
|
||||
)
|
||||
set(radarOptionData, 'series', [
|
||||
{
|
||||
name: `xxx${t('workplace.index')}`,
|
||||
type: 'radar',
|
||||
data: [
|
||||
{
|
||||
value: res.data.map((v) => v.personal),
|
||||
name: t('workplace.personal')
|
||||
},
|
||||
{
|
||||
value: res.data.map((v) => v.team),
|
||||
name: t('workplace.team')
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
const getAllApi = async () => {
|
||||
await Promise.all([getCount(), getProject(), getDynamic(), getTeam(), getRadar()])
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
getAllApi()
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ElCard shadow="never">
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<ElRow :gutter="20" justify="space-between">
|
||||
<ElCol :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
src="@/assets/imgs/avatar.jpg"
|
||||
alt=""
|
||||
class="w-70px h-70px rounded-[50%] mr-20px"
|
||||
/>
|
||||
<div>
|
||||
<div class="text-20px">
|
||||
{{ t('workplace.goodMorning') }},Archer,{{ t('workplace.happyDay') }}
|
||||
</div>
|
||||
<div class="mt-10px text-14px text-gray-500">
|
||||
{{ t('workplace.toady') }},20℃ - 32℃!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElCol>
|
||||
<ElCol :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex h-70px items-center justify-end lt-sm:mt-20px">
|
||||
<div class="px-8px text-right">
|
||||
<div class="text-14px text-gray-400 mb-20px">{{ t('workplace.project') }}</div>
|
||||
<CountTo
|
||||
class="text-20px"
|
||||
:start-val="0"
|
||||
:end-val="totalSate.project"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
<ElDivider direction="vertical" />
|
||||
<div class="px-8px text-right">
|
||||
<div class="text-14px text-gray-400 mb-20px">{{ t('workplace.toDo') }}</div>
|
||||
<CountTo
|
||||
class="text-20px"
|
||||
:start-val="0"
|
||||
:end-val="totalSate.todo"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
<ElDivider direction="vertical" border-style="dashed" />
|
||||
<div class="px-8px text-right">
|
||||
<div class="text-14px text-gray-400 mb-20px">{{ t('workplace.access') }}</div>
|
||||
<CountTo
|
||||
class="text-20px"
|
||||
:start-val="0"
|
||||
:end-val="totalSate.access"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</div>
|
||||
|
||||
<ElRow class="mt-20px" :gutter="20" justify="space-between">
|
||||
<ElCol :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-20px">
|
||||
<ElCard shadow="never">
|
||||
<template #header>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ t('workplace.project') }}</span>
|
||||
<ElLink type="primary" :underline="false">{{ t('workplace.more') }}</ElLink>
|
||||
</div>
|
||||
</template>
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<ElRow>
|
||||
<ElCol
|
||||
v-for="(item, index) in projects"
|
||||
:key="`card-${index}`"
|
||||
:xl="8"
|
||||
:lg="8"
|
||||
:md="12"
|
||||
:sm="24"
|
||||
:xs="24"
|
||||
>
|
||||
<ElCard shadow="hover">
|
||||
<div class="flex items-center">
|
||||
<Icon :icon="item.icon" :size="25" class="mr-10px" />
|
||||
<span class="text-16px">{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="mt-15px text-14px text-gray-400">{{ t(item.message) }}</div>
|
||||
<div class="mt-20px text-12px text-gray-400 flex justify-between">
|
||||
<span>{{ item.personal }}</span>
|
||||
<span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span>
|
||||
</div>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
|
||||
<ElCard shadow="never" class="mt-20px">
|
||||
<template #header>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ t('workplace.dynamic') }}</span>
|
||||
<ElLink type="primary" :underline="false">{{ t('workplace.more') }}</ElLink>
|
||||
</div>
|
||||
</template>
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<div v-for="(item, index) in dynamics" :key="`dynamics-${index}`">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
src="@/assets/imgs/avatar.jpg"
|
||||
alt=""
|
||||
class="w-35px h-35px rounded-[50%] mr-20px"
|
||||
/>
|
||||
<div>
|
||||
<div class="text-14px">
|
||||
<Highlight :keys="item.keys.map((v) => t(v))">
|
||||
{{ t('workplace.pushCode') }}
|
||||
</Highlight>
|
||||
</div>
|
||||
<div class="mt-15px text-12px text-gray-400">
|
||||
{{ useTimeAgo(item.time) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ElDivider />
|
||||
</div>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
<ElCol :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-20px">
|
||||
<ElCard shadow="never">
|
||||
<template #header>
|
||||
<span>{{ t('workplace.shortcutOperation') }}</span>
|
||||
</template>
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<ElRow>
|
||||
<ElCol
|
||||
v-for="item in 9"
|
||||
:key="`card-${item}`"
|
||||
:xl="12"
|
||||
:lg="12"
|
||||
:md="12"
|
||||
:sm="24"
|
||||
:xs="24"
|
||||
class="mb-10px"
|
||||
>
|
||||
<ElLink type="default" :underline="false">
|
||||
{{ t('workplace.operation') }}{{ item }}
|
||||
</ElLink>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
|
||||
<ElCard shadow="never" class="mt-20px">
|
||||
<template #header>
|
||||
<span>xx{{ t('workplace.index') }}</span>
|
||||
</template>
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<Echart :options="radarOptionData" :height="400" />
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
|
||||
<ElCard shadow="never" class="mt-20px">
|
||||
<template #header>
|
||||
<span>{{ t('workplace.team') }}</span>
|
||||
</template>
|
||||
<ElSkeleton :loading="loading" animated>
|
||||
<ElRow>
|
||||
<ElCol v-for="item in team" :key="`team-${item.name}`" :span="12" class="mb-20px">
|
||||
<div class="flex items-center">
|
||||
<Icon :icon="item.icon" class="mr-10px" />
|
||||
<ElLink type="default" :underline="false">
|
||||
{{ item.name }}
|
||||
</ElLink>
|
||||
</div>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
|
@ -1,200 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ElRow, ElCol, ElCard, ElSkeleton } from 'element-plus'
|
||||
import { CountTo } from '@/components/CountTo'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { getCountApi } from '@/api/dashboard/analysis'
|
||||
import type { AnalysisTotalTypes } from '@/api/dashboard/analysis/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('panel')
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
let totalState = reactive<AnalysisTotalTypes>({
|
||||
users: 0,
|
||||
messages: 0,
|
||||
moneys: 0,
|
||||
shoppings: 0
|
||||
})
|
||||
|
||||
const getCount = async () => {
|
||||
const res = await getCountApi()
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
totalState = Object.assign(totalState, res?.data || {})
|
||||
}
|
||||
|
||||
getCount()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElRow :gutter="20" justify="space-between" :class="prefixCls">
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="2">
|
||||
<template #default>
|
||||
<div :class="`${prefixCls}__item flex justify-between`">
|
||||
<div>
|
||||
<div
|
||||
:class="`${prefixCls}__item--icon ${prefixCls}__item--peoples p-16px inline-block rounded-6px`"
|
||||
>
|
||||
<Icon icon="svg-icon:peoples" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
|
||||
t('analysis.newUser')
|
||||
}}</div>
|
||||
<CountTo
|
||||
class="text-20px font-700 text-right"
|
||||
:start-val="0"
|
||||
:end-val="102400"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="2">
|
||||
<template #default>
|
||||
<div :class="`${prefixCls}__item flex justify-between`">
|
||||
<div>
|
||||
<div
|
||||
:class="`${prefixCls}__item--icon ${prefixCls}__item--message p-16px inline-block rounded-6px`"
|
||||
>
|
||||
<Icon icon="svg-icon:message" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
|
||||
t('analysis.unreadInformation')
|
||||
}}</div>
|
||||
<CountTo
|
||||
class="text-20px font-700 text-right"
|
||||
:start-val="0"
|
||||
:end-val="81212"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="2">
|
||||
<template #default>
|
||||
<div :class="`${prefixCls}__item flex justify-between`">
|
||||
<div>
|
||||
<div
|
||||
:class="`${prefixCls}__item--icon ${prefixCls}__item--money p-16px inline-block rounded-6px`"
|
||||
>
|
||||
<Icon icon="svg-icon:money" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
|
||||
t('analysis.transactionAmount')
|
||||
}}</div>
|
||||
<CountTo
|
||||
class="text-20px font-700 text-right"
|
||||
:start-val="0"
|
||||
:end-val="9280"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
<ElCol :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-20px">
|
||||
<ElSkeleton :loading="loading" animated :rows="2">
|
||||
<template #default>
|
||||
<div :class="`${prefixCls}__item flex justify-between`">
|
||||
<div>
|
||||
<div
|
||||
:class="`${prefixCls}__item--icon ${prefixCls}__item--shopping p-16px inline-block rounded-6px`"
|
||||
>
|
||||
<Icon icon="svg-icon:shopping" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div :class="`${prefixCls}__item--text text-16px text-gray-500 text-right`">{{
|
||||
t('analysis.totalShopping')
|
||||
}}</div>
|
||||
<CountTo
|
||||
class="text-20px font-700 text-right"
|
||||
:start-val="0"
|
||||
:end-val="13600"
|
||||
:duration="2600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElSkeleton>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-panel';
|
||||
|
||||
.@{prefix-cls} {
|
||||
&__item {
|
||||
&--peoples {
|
||||
color: #40c9c6;
|
||||
}
|
||||
|
||||
&--message {
|
||||
color: #36a3f7;
|
||||
}
|
||||
|
||||
&--money {
|
||||
color: #f4516c;
|
||||
}
|
||||
|
||||
&--shopping {
|
||||
color: #34bfa3;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
:deep(.@{namespace}-icon) {
|
||||
color: #fff !important;
|
||||
}
|
||||
.@{prefix-cls}__item--icon {
|
||||
transition: all 0.38s ease-out;
|
||||
}
|
||||
.@{prefix-cls}__item--peoples {
|
||||
background: #40c9c6;
|
||||
}
|
||||
.@{prefix-cls}__item--message {
|
||||
background: #36a3f7;
|
||||
}
|
||||
.@{prefix-cls}__item--money {
|
||||
background: #f4516c;
|
||||
}
|
||||
.@{prefix-cls}__item--shopping {
|
||||
background: #34bfa3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,309 +0,0 @@
|
|||
import { EChartsOption } from 'echarts'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
export const lineOptions: EChartsOption = {
|
||||
title: {
|
||||
text: t('analysis.monthlySales'),
|
||||
left: 'center'
|
||||
},
|
||||
xAxis: {
|
||||
data: [
|
||||
t('analysis.january'),
|
||||
t('analysis.february'),
|
||||
t('analysis.march'),
|
||||
t('analysis.april'),
|
||||
t('analysis.may'),
|
||||
t('analysis.june'),
|
||||
t('analysis.july'),
|
||||
t('analysis.august'),
|
||||
t('analysis.september'),
|
||||
t('analysis.october'),
|
||||
t('analysis.november'),
|
||||
t('analysis.december')
|
||||
],
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
top: 80,
|
||||
containLabel: true
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
padding: [5, 10]
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: [t('analysis.estimate'), t('analysis.actual')],
|
||||
top: 50
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t('analysis.estimate'),
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123],
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'cubicInOut'
|
||||
},
|
||||
{
|
||||
name: t('analysis.actual'),
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
itemStyle: {},
|
||||
data: [120, 82, 91, 154, 162, 140, 145, 250, 134, 56, 99, 123],
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'quadraticOut'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const pieOptions: EChartsOption = {
|
||||
title: {
|
||||
text: t('analysis.userAccessSource'),
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: [
|
||||
t('analysis.directAccess'),
|
||||
t('analysis.mailMarketing'),
|
||||
t('analysis.allianceAdvertising'),
|
||||
t('analysis.videoAdvertising'),
|
||||
t('analysis.searchEngines')
|
||||
]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t('analysis.userAccessSource'),
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['50%', '60%'],
|
||||
data: [
|
||||
{ value: 335, name: t('analysis.directAccess') },
|
||||
{ value: 310, name: t('analysis.mailMarketing') },
|
||||
{ value: 234, name: t('analysis.allianceAdvertising') },
|
||||
{ value: 135, name: t('analysis.videoAdvertising') },
|
||||
{ value: 1548, name: t('analysis.searchEngines') }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const barOptions: EChartsOption = {
|
||||
title: {
|
||||
text: t('analysis.weeklyUserActivity'),
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 50,
|
||||
right: 20,
|
||||
bottom: 20
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: [
|
||||
t('analysis.monday'),
|
||||
t('analysis.tuesday'),
|
||||
t('analysis.wednesday'),
|
||||
t('analysis.thursday'),
|
||||
t('analysis.friday'),
|
||||
t('analysis.saturday'),
|
||||
t('analysis.sunday')
|
||||
],
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t('analysis.activeQuantity'),
|
||||
data: [13253, 34235, 26321, 12340, 24643, 1322, 1324],
|
||||
type: 'bar'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const radarOption: EChartsOption = {
|
||||
legend: {
|
||||
data: [t('workplace.personal'), t('workplace.team')]
|
||||
},
|
||||
radar: {
|
||||
// shape: 'circle',
|
||||
indicator: [
|
||||
{ name: t('workplace.quote'), max: 65 },
|
||||
{ name: t('workplace.contribution'), max: 160 },
|
||||
{ name: t('workplace.hot'), max: 300 },
|
||||
{ name: t('workplace.yield'), max: 130 },
|
||||
{ name: t('workplace.follow'), max: 100 }
|
||||
]
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: `xxx${t('workplace.index')}`,
|
||||
type: 'radar',
|
||||
data: [
|
||||
{
|
||||
value: [42, 30, 20, 35, 80],
|
||||
name: t('workplace.personal')
|
||||
},
|
||||
{
|
||||
value: [50, 140, 290, 100, 90],
|
||||
name: t('workplace.team')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const wordOptions = {
|
||||
series: [
|
||||
{
|
||||
type: 'wordCloud',
|
||||
gridSize: 2,
|
||||
sizeRange: [12, 50],
|
||||
rotationRange: [-90, 90],
|
||||
shape: 'pentagon',
|
||||
width: 600,
|
||||
height: 400,
|
||||
drawOutOfBound: true,
|
||||
textStyle: {
|
||||
color: function () {
|
||||
return (
|
||||
'rgb(' +
|
||||
[
|
||||
Math.round(Math.random() * 160),
|
||||
Math.round(Math.random() * 160),
|
||||
Math.round(Math.random() * 160)
|
||||
].join(',') +
|
||||
')'
|
||||
)
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
textStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: '#333'
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
name: 'Sam S Club',
|
||||
value: 10000,
|
||||
textStyle: {
|
||||
color: 'black'
|
||||
},
|
||||
emphasis: {
|
||||
textStyle: {
|
||||
color: 'red'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Macys',
|
||||
value: 6181
|
||||
},
|
||||
{
|
||||
name: 'Amy Schumer',
|
||||
value: 4386
|
||||
},
|
||||
{
|
||||
name: 'Jurassic World',
|
||||
value: 4055
|
||||
},
|
||||
{
|
||||
name: 'Charter Communications',
|
||||
value: 2467
|
||||
},
|
||||
{
|
||||
name: 'Chick Fil A',
|
||||
value: 2244
|
||||
},
|
||||
{
|
||||
name: 'Planet Fitness',
|
||||
value: 1898
|
||||
},
|
||||
{
|
||||
name: 'Pitch Perfect',
|
||||
value: 1484
|
||||
},
|
||||
{
|
||||
name: 'Express',
|
||||
value: 1112
|
||||
},
|
||||
{
|
||||
name: 'Home',
|
||||
value: 965
|
||||
},
|
||||
{
|
||||
name: 'Johnny Depp',
|
||||
value: 847
|
||||
},
|
||||
{
|
||||
name: 'Lena Dunham',
|
||||
value: 582
|
||||
},
|
||||
{
|
||||
name: 'Lewis Hamilton',
|
||||
value: 555
|
||||
},
|
||||
{
|
||||
name: 'KXAN',
|
||||
value: 550
|
||||
},
|
||||
{
|
||||
name: 'Mary Ellen Mark',
|
||||
value: 462
|
||||
},
|
||||
{
|
||||
name: 'Farrah Abraham',
|
||||
value: 366
|
||||
},
|
||||
{
|
||||
name: 'Rita Ora',
|
||||
value: 360
|
||||
},
|
||||
{
|
||||
name: 'Serena Williams',
|
||||
value: 282
|
||||
},
|
||||
{
|
||||
name: 'NCAA baseball tournament',
|
||||
value: 273
|
||||
},
|
||||
{
|
||||
name: 'Point Break',
|
||||
value: 265
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { Error } from '@/components/Error'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { push } = useRouter()
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const errorClick = () => {
|
||||
push(permissionStore.addRouters[0]?.path as string)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Error type="403" @error-click="errorClick" />
|
||||
</template>
|
|
@ -1,17 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { Error } from '@/components/Error'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { push } = useRouter()
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const errorClick = () => {
|
||||
push(permissionStore.addRouters[0]?.path as string)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Error @error-click="errorClick" />
|
||||
</template>
|
|
@ -1,17 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { Error } from '@/components/Error'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const { push } = useRouter()
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const errorClick = () => {
|
||||
push(permissionStore.addRouters[0]?.path as string)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Error type="500" @error-click="errorClick" />
|
||||
</template>
|
|
@ -1,324 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { Search } from '@/components/Search'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ElButton, ElTag } from 'element-plus'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { getTableListApi, saveTableApi, delTableListApi } from '@/api/table'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { h, ref, unref, reactive } from 'vue'
|
||||
import Write from './components/Write.vue'
|
||||
import Detail from './components/Detail.vue'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
|
||||
const ids = ref<string[]>([])
|
||||
|
||||
const { tableRegister, tableState, tableMethods } = useTable({
|
||||
fetchDataApi: async () => {
|
||||
const { currentPage, pageSize } = tableState
|
||||
const res = await getTableListApi({
|
||||
pageIndex: unref(currentPage),
|
||||
pageSize: unref(pageSize),
|
||||
...unref(searchParams)
|
||||
})
|
||||
return {
|
||||
list: res.data.list,
|
||||
total: res.data.total
|
||||
}
|
||||
},
|
||||
fetchDelApi: async () => {
|
||||
const res = await delTableListApi(unref(ids))
|
||||
return !!res
|
||||
}
|
||||
})
|
||||
const { loading, dataList, total, currentPage, pageSize } = tableState
|
||||
const { getList, getElTableExpose, delList } = tableMethods
|
||||
|
||||
const searchParams = ref({})
|
||||
const setSearchParams = (params: any) => {
|
||||
searchParams.value = params
|
||||
getList()
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
field: 'selection',
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
},
|
||||
table: {
|
||||
type: 'selection'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'index',
|
||||
label: t('tableDemo.index'),
|
||||
type: 'index',
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
label: t('tableDemo.title'),
|
||||
search: {
|
||||
component: 'Input'
|
||||
},
|
||||
form: {
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('tableDemo.author')
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('tableDemo.displayTime'),
|
||||
form: {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'datetime',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('tableDemo.importance'),
|
||||
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
|
||||
return h(
|
||||
ElTag,
|
||||
{
|
||||
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
|
||||
},
|
||||
() =>
|
||||
cellValue === 1
|
||||
? t('tableDemo.important')
|
||||
: cellValue === 2
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')
|
||||
)
|
||||
},
|
||||
form: {
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: '重要',
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: '良好',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '一般',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
return (
|
||||
<ElTag
|
||||
type={
|
||||
data.importance === 1 ? 'success' : data.importance === 2 ? 'warning' : 'danger'
|
||||
}
|
||||
>
|
||||
{data.importance === 1
|
||||
? t('tableDemo.important')
|
||||
: data.importance === 2
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')}
|
||||
</ElTag>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('tableDemo.pageviews'),
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: t('exampleDemo.content'),
|
||||
table: {
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
component: 'Editor',
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
span: 24,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
return <div innerHTML={data.content}></div>
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '260px',
|
||||
label: t('tableDemo.action'),
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
},
|
||||
table: {
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" onClick={() => action(data[0].row, 'edit')}>
|
||||
{t('exampleDemo.edit')}
|
||||
</ElButton>
|
||||
<ElButton type="success" onClick={() => action(data[0].row, 'detail')}>
|
||||
{t('exampleDemo.detail')}
|
||||
</ElButton>
|
||||
<ElButton type="danger" onClick={() => delData(data[0].row)}>
|
||||
{t('exampleDemo.del')}
|
||||
</ElButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
// @ts-ignore
|
||||
const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
|
||||
const currentRow = ref<TableData | null>(null)
|
||||
const actionType = ref('')
|
||||
|
||||
const AddAction = () => {
|
||||
dialogTitle.value = t('exampleDemo.add')
|
||||
currentRow.value = null
|
||||
dialogVisible.value = true
|
||||
actionType.value = ''
|
||||
}
|
||||
|
||||
const delLoading = ref(false)
|
||||
|
||||
const delData = async (row: TableData | null) => {
|
||||
const elTableExpose = await getElTableExpose()
|
||||
ids.value = row ? [row.id] : elTableExpose?.getSelectionRows().map((v: TableData) => v.id) || []
|
||||
delLoading.value = true
|
||||
await delList(unref(ids).length).finally(() => {
|
||||
delLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const action = (row: TableData, type: string) => {
|
||||
dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
|
||||
actionType.value = type
|
||||
currentRow.value = row
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const writeRef = ref<ComponentRef<typeof Write>>()
|
||||
|
||||
const saveLoading = ref(false)
|
||||
|
||||
const save = async () => {
|
||||
const write = unref(writeRef)
|
||||
const formData = await write?.submit()
|
||||
if (formData) {
|
||||
saveLoading.value = true
|
||||
const res = await saveTableApi(formData)
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
saveLoading.value = false
|
||||
})
|
||||
if (res) {
|
||||
dialogVisible.value = false
|
||||
currentPage.value = 1
|
||||
getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
|
||||
<div class="mb-10px">
|
||||
<ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
|
||||
<ElButton :loading="delLoading" type="danger" @click="delData(null)">
|
||||
{{ t('exampleDemo.del') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
v-model:pageSize="pageSize"
|
||||
v-model:currentPage="currentPage"
|
||||
:columns="allSchemas.tableColumns"
|
||||
:data="dataList"
|
||||
:loading="loading"
|
||||
:pagination="{
|
||||
total: total
|
||||
}"
|
||||
@register="tableRegister"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<Write
|
||||
v-if="actionType !== 'detail'"
|
||||
ref="writeRef"
|
||||
:form-schema="allSchemas.formSchema"
|
||||
:current-row="currentRow"
|
||||
/>
|
||||
|
||||
<Detail
|
||||
v-if="actionType === 'detail'"
|
||||
:detail-schema="allSchemas.detailSchema"
|
||||
:current-row="currentRow"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<ElButton v-if="actionType !== 'detail'" type="primary" :loading="saveLoading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
|
@ -1,20 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
import type { TableData } from '@/api/table/types'
|
||||
import { Descriptions, DescriptionsSchema } from '@/components/Descriptions'
|
||||
|
||||
defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<Nullable<TableData>>,
|
||||
default: () => null
|
||||
},
|
||||
detailSchema: {
|
||||
type: Array as PropType<DescriptionsSchema[]>,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Descriptions :schema="detailSchema" :data="currentRow || {}" />
|
||||
</template>
|
|
@ -1,63 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { PropType, reactive, watch } from 'vue'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const props = defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<Nullable<TableData>>,
|
||||
default: () => null
|
||||
},
|
||||
formSchema: {
|
||||
type: Array as PropType<FormSchema[]>,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
title: [required()],
|
||||
author: [required()],
|
||||
importance: [required()],
|
||||
pageviews: [required()],
|
||||
display_time: [required()],
|
||||
content: [required()]
|
||||
})
|
||||
|
||||
const { formRegister, formMethods } = useForm()
|
||||
const { setValues, getFormData, getElFormExpose } = formMethods
|
||||
|
||||
const submit = async () => {
|
||||
const elForm = await getElFormExpose()
|
||||
const valid = await elForm?.validate().catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
if (valid) {
|
||||
const formData = getFormData()
|
||||
return formData
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.currentRow,
|
||||
(currentRow) => {
|
||||
if (!currentRow) return
|
||||
setValues(currentRow)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
defineExpose({
|
||||
submit
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form :rules="rules" @register="formRegister" :schema="formSchema" />
|
||||
</template>
|
|
@ -1,53 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import Write from './components/Write.vue'
|
||||
import { ContentDetailWrap } from '@/components/ContentDetailWrap'
|
||||
import { ref, unref } from 'vue'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { saveTableApi } from '@/api/table'
|
||||
import { useEmitt } from '@/hooks/event/useEmitt'
|
||||
|
||||
const { emitter } = useEmitt()
|
||||
|
||||
const { push, go } = useRouter()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const writeRef = ref<ComponentRef<typeof Write>>()
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const save = async () => {
|
||||
const write = unref(writeRef)
|
||||
const formData = await write?.submit()
|
||||
if (formData) {
|
||||
loading.value = true
|
||||
const res = await saveTableApi(formData)
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
if (res) {
|
||||
emitter.emit('getList', 'add')
|
||||
push('/example/example-page')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentDetailWrap :title="t('exampleDemo.add')" @back="push('/example/example-page')">
|
||||
<Write ref="writeRef" />
|
||||
|
||||
<template #header>
|
||||
<ElButton @click="go(-1)">
|
||||
{{ t('common.back') }}
|
||||
</ElButton>
|
||||
<ElButton type="primary" :loading="loading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ContentDetailWrap>
|
||||
</template>
|
||||
@/hooks/event/useEmitt
|
|
@ -1,38 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import Detail from './components/Detail.vue'
|
||||
import { ContentDetailWrap } from '@/components/ContentDetailWrap'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { getTableDetApi } from '@/api/table'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { ElButton } from 'element-plus'
|
||||
|
||||
const { push, go } = useRouter()
|
||||
|
||||
const { query } = useRoute()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const currentRow = ref<Nullable<TableData>>(null)
|
||||
|
||||
const getTableDet = async () => {
|
||||
const res = await getTableDetApi(query.id as string)
|
||||
if (res) {
|
||||
currentRow.value = res.data
|
||||
}
|
||||
}
|
||||
|
||||
getTableDet()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentDetailWrap :title="t('exampleDemo.detail')" @back="push('/example/example-page')">
|
||||
<template #header>
|
||||
<ElButton @click="go(-1)">
|
||||
{{ t('common.back') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
<Detail :current-row="currentRow" />
|
||||
</ContentDetailWrap>
|
||||
</template>
|
|
@ -1,67 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import Write from './components/Write.vue'
|
||||
import { ContentDetailWrap } from '@/components/ContentDetailWrap'
|
||||
import { ref, unref } from 'vue'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { saveTableApi, getTableDetApi } from '@/api/table'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { useEmitt } from '@/hooks/event/useEmitt'
|
||||
|
||||
const { emitter } = useEmitt()
|
||||
|
||||
const { push, go } = useRouter()
|
||||
|
||||
const { query } = useRoute()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const currentRow = ref<Nullable<TableData>>(null)
|
||||
|
||||
const getTableDet = async () => {
|
||||
const res = await getTableDetApi(query.id as string)
|
||||
if (res) {
|
||||
currentRow.value = res.data
|
||||
}
|
||||
}
|
||||
|
||||
getTableDet()
|
||||
|
||||
const writeRef = ref<ComponentRef<typeof Write>>()
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const save = async () => {
|
||||
const write = unref(writeRef)
|
||||
const formData = await write?.submit()
|
||||
if (formData) {
|
||||
loading.value = true
|
||||
const res = await saveTableApi(formData)
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
if (res) {
|
||||
emitter.emit('getList', 'editor')
|
||||
push('/example/example-page')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentDetailWrap :title="t('exampleDemo.edit')" @back="push('/example/example-page')">
|
||||
<Write ref="writeRef" :current-row="currentRow" />
|
||||
|
||||
<template #header>
|
||||
<ElButton @click="go(-1)">
|
||||
{{ t('common.back') }}
|
||||
</ElButton>
|
||||
<ElButton type="primary" :loading="loading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ContentDetailWrap>
|
||||
</template>
|
||||
@/hooks/event/useEmitt
|
|
@ -1,262 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { Search } from '@/components/Search'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ElButton, ElTag } from 'element-plus'
|
||||
import { Table, TableColumn } from '@/components/Table'
|
||||
import { getTableListApi, delTableListApi } from '@/api/table'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { h, reactive, ref, unref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useEmitt } from '@/hooks/event/useEmitt'
|
||||
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
|
||||
defineOptions({
|
||||
name: 'ExamplePage'
|
||||
})
|
||||
|
||||
const { push } = useRouter()
|
||||
|
||||
const ids = ref<string[]>([])
|
||||
|
||||
const searchParams = ref({})
|
||||
const setSearchParams = (params: any) => {
|
||||
searchParams.value = params
|
||||
getList()
|
||||
}
|
||||
|
||||
const { tableRegister, tableState, tableMethods } = useTable({
|
||||
fetchDataApi: async () => {
|
||||
const { currentPage, pageSize } = tableState
|
||||
const res = await getTableListApi({
|
||||
pageIndex: unref(currentPage),
|
||||
pageSize: unref(pageSize),
|
||||
...unref(searchParams)
|
||||
})
|
||||
return {
|
||||
list: res.data.list,
|
||||
total: res.data.total
|
||||
}
|
||||
},
|
||||
fetchDelApi: async () => {
|
||||
const res = await delTableListApi(unref(ids))
|
||||
return !!res
|
||||
}
|
||||
})
|
||||
const { loading, dataList, total, currentPage, pageSize } = tableState
|
||||
const { getList, getElTableExpose, delList } = tableMethods
|
||||
|
||||
getList()
|
||||
|
||||
useEmitt({
|
||||
name: 'getList',
|
||||
callback: (type: string) => {
|
||||
if (type === 'add') {
|
||||
currentPage.value = 1
|
||||
}
|
||||
getList()
|
||||
}
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
{
|
||||
field: 'selection',
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
},
|
||||
table: {
|
||||
type: 'selection'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'index',
|
||||
label: t('tableDemo.index'),
|
||||
type: 'index',
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
label: t('tableDemo.title'),
|
||||
search: {
|
||||
component: 'Input'
|
||||
},
|
||||
form: {
|
||||
component: 'Input',
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('tableDemo.author')
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('tableDemo.displayTime'),
|
||||
form: {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'datetime',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('tableDemo.importance'),
|
||||
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
|
||||
return h(
|
||||
ElTag,
|
||||
{
|
||||
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
|
||||
},
|
||||
() =>
|
||||
cellValue === 1
|
||||
? t('tableDemo.important')
|
||||
: cellValue === 2
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')
|
||||
)
|
||||
},
|
||||
form: {
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: '重要',
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: '良好',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '一般',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('tableDemo.pageviews'),
|
||||
form: {
|
||||
component: 'InputNumber',
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: t('exampleDemo.content'),
|
||||
table: {
|
||||
hidden: true
|
||||
},
|
||||
form: {
|
||||
component: 'Editor',
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
detail: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '260px',
|
||||
label: t('tableDemo.action'),
|
||||
form: {
|
||||
hidden: true
|
||||
},
|
||||
detail: {
|
||||
hidden: true
|
||||
},
|
||||
table: {
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
return (
|
||||
<>
|
||||
<ElButton type="primary" onClick={() => action(data[0].row, 'edit')}>
|
||||
{t('exampleDemo.edit')}
|
||||
</ElButton>
|
||||
<ElButton type="success" onClick={() => action(data[0].row, 'detail')}>
|
||||
{t('exampleDemo.detail')}
|
||||
</ElButton>
|
||||
<ElButton type="danger" onClick={() => delData(data[0].row)}>
|
||||
{t('exampleDemo.del')}
|
||||
</ElButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
// @ts-ignore
|
||||
const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
|
||||
const AddAction = () => {
|
||||
push('/example/example-add')
|
||||
}
|
||||
|
||||
const delLoading = ref(false)
|
||||
|
||||
const delData = async (row: TableData | null) => {
|
||||
const elTableExpose = await getElTableExpose()
|
||||
ids.value = row ? [row.id] : elTableExpose?.getSelectionRows().map((v: TableData) => v.id) || []
|
||||
delLoading.value = true
|
||||
await delList(unref(ids).length).finally(() => {
|
||||
delLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const action = (row: TableData, type: string) => {
|
||||
push(`/example/example-${type}?id=${row.id}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
|
||||
<div class="mb-10px">
|
||||
<ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
|
||||
<ElButton :loading="delLoading" type="danger" @click="delData(null)">
|
||||
{{ t('exampleDemo.del') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
v-model:pageSize="pageSize"
|
||||
v-model:currentPage="currentPage"
|
||||
:columns="allSchemas.tableColumns"
|
||||
:data="dataList"
|
||||
:loading="loading"
|
||||
:pagination="{
|
||||
total: total
|
||||
}"
|
||||
@register="tableRegister"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
@/hooks/event/useEmitt
|
|
@ -1,69 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { PropType, reactive } from 'vue'
|
||||
import type { TableData } from '@/api/table/types'
|
||||
import { Descriptions, DescriptionsSchema } from '@/components/Descriptions'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ElTag } from 'element-plus'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<Nullable<TableData>>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const schema = reactive<DescriptionsSchema[]>([
|
||||
{
|
||||
field: 'title',
|
||||
label: t('exampleDemo.title'),
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('exampleDemo.author')
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('exampleDemo.displayTime')
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('exampleDemo.importance'),
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
return (
|
||||
<ElTag
|
||||
type={data.importance === 1 ? 'success' : data.importance === 2 ? 'warning' : 'danger'}
|
||||
>
|
||||
{data.importance === 1
|
||||
? t('tableDemo.important')
|
||||
: data.importance === 2
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')}
|
||||
</ElTag>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('exampleDemo.pageviews')
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: t('exampleDemo.content'),
|
||||
span: 24,
|
||||
slots: {
|
||||
default: (data: any) => {
|
||||
return <div innerHTML={data.content}></div>
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Descriptions :schema="schema" :data="currentRow || {}" />
|
||||
</template>
|
|
@ -1,154 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { Form, FormSchema } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { PropType, reactive, watch } from 'vue'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { IDomEditor } from '@wangeditor/editor'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const props = defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<Nullable<TableData>>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { formRegister, formMethods } = useForm()
|
||||
const { setValues, getFormData, getElFormExpose, setSchema } = formMethods
|
||||
|
||||
const schema = reactive<FormSchema[]>([
|
||||
{
|
||||
field: 'title',
|
||||
label: t('exampleDemo.title'),
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required()]
|
||||
},
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('exampleDemo.author'),
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [required()]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('exampleDemo.displayTime'),
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'datetime',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss'
|
||||
},
|
||||
formItemProps: {
|
||||
rules: [required()]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('exampleDemo.importance'),
|
||||
component: 'Select',
|
||||
formItemProps: {
|
||||
rules: [required()]
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '重要',
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
label: '良好',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '一般',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('exampleDemo.pageviews'),
|
||||
component: 'InputNumber',
|
||||
value: 0,
|
||||
formItemProps: {
|
||||
rules: [required()]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
component: 'Editor',
|
||||
colProps: {
|
||||
span: 24
|
||||
},
|
||||
componentProps: {
|
||||
defaultHtml: '',
|
||||
// @ts-ignore
|
||||
onChange: (edit: IDomEditor) => {
|
||||
setValues({
|
||||
content: edit.getHtml()
|
||||
})
|
||||
}
|
||||
},
|
||||
label: t('exampleDemo.content')
|
||||
}
|
||||
])
|
||||
|
||||
const rules = reactive({
|
||||
title: [required()],
|
||||
author: [required()],
|
||||
importance: [required()],
|
||||
pageviews: [required()],
|
||||
display_time: [required()],
|
||||
content: [required()]
|
||||
})
|
||||
|
||||
const submit = async () => {
|
||||
const elForm = await getElFormExpose()
|
||||
const valid = await elForm?.validate().catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
if (valid) {
|
||||
const formData = getFormData()
|
||||
return formData
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.currentRow,
|
||||
(currentRow) => {
|
||||
if (!currentRow) return
|
||||
setValues(currentRow)
|
||||
setSchema([
|
||||
{
|
||||
field: 'content',
|
||||
path: 'componentProps.defaultHtml',
|
||||
value: currentRow.content
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
defineExpose({
|
||||
submit
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form :rules="rules" @register="formRegister" :schema="schema" />
|
||||
</template>
|
|
@ -1,20 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useIntro } from '@/hooks/web/useIntro'
|
||||
import { ElButton } from 'element-plus'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { introRef } = useIntro()
|
||||
|
||||
const guideStart = () => {
|
||||
introRef.start()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('guideDemo.guide')" :message="t('guideDemo.message')">
|
||||
<ElButton type="primary" @click="guideStart">{{ t('guideDemo.start') }}</ElButton>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,223 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
// import { ContentWrap } from '@/components/ContentWrap'
|
||||
// import { Search } from '@/components/Search'
|
||||
// import { useI18n } from '@/hooks/web/useI18n'
|
||||
// import { ElButton, ElTag } from 'element-plus'
|
||||
// import { Table } from '@/components/Table'
|
||||
// import { getTableListApi, delTableListApi } from '@/api/table'
|
||||
// import { useTable } from '@/hooks/web/useTable'
|
||||
// import { TableData } from '@/api/table/types'
|
||||
// import { h, ref, reactive } from 'vue'
|
||||
// import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
|
||||
// import { useDictStore } from '@/store/modules/dict'
|
||||
// import { getDictOneApi } from '@/api/common'
|
||||
// import { TableColumn } from '@/types/table'
|
||||
|
||||
// const dictStore = useDictStore()
|
||||
|
||||
// const { register, tableObject, methods } = useTable<TableData>({
|
||||
// getListApi: getTableListApi,
|
||||
// delListApi: delTableListApi,
|
||||
// response: {
|
||||
// list: 'list',
|
||||
// total: 'total'
|
||||
// }
|
||||
// })
|
||||
|
||||
// const { getList, setSearchParams } = methods
|
||||
|
||||
// getList()
|
||||
|
||||
// const { t } = useI18n()
|
||||
|
||||
// const crudSchemas = reactive<CrudSchema[]>([
|
||||
// {
|
||||
// field: 'index',
|
||||
// label: t('tableDemo.index'),
|
||||
// type: 'index',
|
||||
// form: {
|
||||
// show: false
|
||||
// },
|
||||
// detail: {
|
||||
// show: false
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'title',
|
||||
// label: t('tableDemo.title'),
|
||||
// search: {
|
||||
// show: true
|
||||
// },
|
||||
// form: {
|
||||
// colProps: {
|
||||
// span: 24
|
||||
// }
|
||||
// },
|
||||
// detail: {
|
||||
// span: 24
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'author',
|
||||
// label: t('tableDemo.author')
|
||||
// },
|
||||
// {
|
||||
// field: 'display_time',
|
||||
// label: t('tableDemo.displayTime'),
|
||||
// form: {
|
||||
// component: 'DatePicker',
|
||||
// componentProps: {
|
||||
// type: 'datetime',
|
||||
// valueFormat: 'YYYY-MM-DD HH:mm:ss'
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'importance',
|
||||
// label: t('tableDemo.importance'),
|
||||
// formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
|
||||
// return h(
|
||||
// ElTag,
|
||||
// {
|
||||
// type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
|
||||
// },
|
||||
// () =>
|
||||
// cellValue === 1
|
||||
// ? t('tableDemo.important')
|
||||
// : cellValue === 2
|
||||
// ? t('tableDemo.good')
|
||||
// : t('tableDemo.commonly')
|
||||
// )
|
||||
// },
|
||||
// search: {
|
||||
// show: true,
|
||||
// component: 'Select',
|
||||
// componentProps: {
|
||||
// options: dictStore.getDictObj.importance
|
||||
// }
|
||||
// },
|
||||
// form: {
|
||||
// component: 'Select',
|
||||
// componentProps: {
|
||||
// options: [
|
||||
// {
|
||||
// label: '重要',
|
||||
// value: 3
|
||||
// },
|
||||
// {
|
||||
// label: '良好',
|
||||
// value: 2
|
||||
// },
|
||||
// {
|
||||
// label: '一般',
|
||||
// value: 1
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'importance2',
|
||||
// label: `${t('tableDemo.importance')}2`,
|
||||
// search: {
|
||||
// show: true,
|
||||
// component: 'Select',
|
||||
// dictName: 'importance'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'importance3',
|
||||
// label: `${t('tableDemo.importance')}3`,
|
||||
// search: {
|
||||
// show: true,
|
||||
// component: 'Select',
|
||||
// api: async () => {
|
||||
// const res = await getDictOneApi()
|
||||
// return res.data
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'pageviews',
|
||||
// label: t('tableDemo.pageviews'),
|
||||
// form: {
|
||||
// component: 'InputNumber',
|
||||
// value: 0
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'content',
|
||||
// label: t('exampleDemo.content'),
|
||||
// table: {
|
||||
// show: false
|
||||
// },
|
||||
// form: {
|
||||
// component: 'Editor',
|
||||
// colProps: {
|
||||
// span: 24
|
||||
// }
|
||||
// },
|
||||
// detail: {
|
||||
// span: 24
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// field: 'action',
|
||||
// width: '260px',
|
||||
// label: t('tableDemo.action'),
|
||||
// form: {
|
||||
// show: false
|
||||
// },
|
||||
// detail: {
|
||||
// show: false
|
||||
// }
|
||||
// }
|
||||
// ])
|
||||
|
||||
// const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
|
||||
// const delLoading = ref(false)
|
||||
|
||||
// const delData = async (row: TableData | null, multiple: boolean) => {
|
||||
// tableObject.currentRow = row
|
||||
// const { delList, getSelections } = methods
|
||||
// const selections = await getSelections()
|
||||
// delLoading.value = true
|
||||
// await delList(
|
||||
// multiple ? selections.map((v) => v.id) : [tableObject.currentRow?.id as string],
|
||||
// multiple
|
||||
// ).finally(() => {
|
||||
// delLoading.value = false
|
||||
// })
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
|
||||
<div class="mb-10px">
|
||||
<ElButton :loading="delLoading" type="danger" @click="delData(null, true)">
|
||||
{{ t('exampleDemo.del') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
:columns="allSchemas.tableColumns"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:pagination="{
|
||||
total: tableObject.total
|
||||
}"
|
||||
@register="register"
|
||||
>
|
||||
<template #action="{ row }">
|
||||
<ElButton type="danger" @click="delData(row, false)">
|
||||
{{ t('exampleDemo.del') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</Table> -->
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -1,32 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { useWatermark } from '@/hooks/web/useWatermark'
|
||||
import { computed, onBeforeUnmount } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const title = computed(() => appStore.getTitle)
|
||||
|
||||
const { setWatermark, clear } = useWatermark()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clear()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap title="useWatermark">
|
||||
<ElButton type="primary" @click="setWatermark(title)">
|
||||
{{ t('watermarkDemo.createdWatermark') }}
|
||||
</ElButton>
|
||||
<ElButton type="danger" @click="clear">{{ t('watermarkDemo.clearWatermark') }}</ElButton>
|
||||
<ElButton type="warning" @click="setWatermark(`New${title}`)">
|
||||
{{ t('watermarkDemo.resetWatermark') }}
|
||||
</ElButton>
|
||||
</ContentWrap>
|
||||
</template>
|
Loading…
Reference in New Issue