feat: 简易版本

This commit is contained in:
kailong321200875 2023-08-05 11:09:40 +08:00
parent 5fc57bdb08
commit 255843d4c0
60 changed files with 0 additions and 7839 deletions

View File

@ -1,3 +0,0 @@
import CountTo from './src/CountTo.vue'
export { CountTo }

View File

@ -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>

View File

@ -1,3 +0,0 @@
import Echart from './src/Echart.vue'
export { Echart }

View File

@ -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>

View File

@ -1,3 +0,0 @@
import Highlight from './src/Highlight.vue'
export { Highlight }

View File

@ -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>

View File

@ -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)
}

View File

@ -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>

View File

@ -1,9 +0,0 @@
export interface ImageViewerProps {
urlList?: string[]
zIndex?: number
initialIndex?: number
infinite?: boolean
hideOnClickModal?: boolean
teleported?: boolean
show?: boolean
}

View File

@ -1,5 +0,0 @@
import Infotip from './src/Infotip.vue'
export type { InfoTipSchema } from './src/types'
export { Infotip }

View File

@ -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>

View File

@ -1,4 +0,0 @@
export interface InfoTipSchema {
label: string
keys?: string[]
}

View File

@ -1,5 +0,0 @@
import Qrcode from './src/Qrcode.vue'
export type { QrcodeLogo } from './src/types'
export { Qrcode }

View File

@ -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,imglogo
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>

View File

@ -1,9 +0,0 @@
export interface QrcodeLogo {
src?: string
logoSize?: number
bgColor?: string
borderSize?: number
crossOrigin?: string
borderRadius?: number
logoRadius?: number
}

View File

@ -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)
}

View File

@ -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

View File

@ -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')
}

View File

@ -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')
}
}
]
}
]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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
}
]
}
]
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>