release: template版本发布
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* request全局配置
|
||||
*/
|
||||
|
||||
const config: {
|
||||
base_url: {
|
||||
base: string
|
||||
dev: string
|
||||
pro: string
|
||||
test: string
|
||||
}
|
||||
result_code: number | string
|
||||
default_headers: 'application/json' | 'application/x-www-form-urlencoded' | 'multipart/form-data'
|
||||
request_timeout: number
|
||||
} = {
|
||||
/**
|
||||
* api请求基础路径
|
||||
*/
|
||||
base_url: {
|
||||
// 开发环境接口前缀
|
||||
base: 'http://mockjs.test.cn',
|
||||
|
||||
// 打包开发环境接口前缀
|
||||
dev: 'http://mockjs.test.cn',
|
||||
|
||||
// 打包生产环境接口前缀
|
||||
pro: 'http://mockjs.test.cn',
|
||||
|
||||
// 打包测试环境接口前缀
|
||||
test: 'http://mockjs.test.cn'
|
||||
},
|
||||
|
||||
/**
|
||||
* 接口成功返回状态码
|
||||
*/
|
||||
result_code: '0000',
|
||||
|
||||
/**
|
||||
* 接口请求超时时间
|
||||
*/
|
||||
request_timeout: 60000,
|
||||
|
||||
/**
|
||||
* 默认接口请求类型
|
||||
* 可选值:application/x-www-form-urlencoded multipart/form-data
|
||||
*/
|
||||
default_headers: 'application/json'
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -1,32 +0,0 @@
|
||||
import request from './request'
|
||||
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
import config from './config'
|
||||
|
||||
import { AxiosPromise } from 'axios'
|
||||
|
||||
const { default_headers } = config
|
||||
|
||||
function fetch({
|
||||
url,
|
||||
method,
|
||||
params,
|
||||
data,
|
||||
headersType,
|
||||
responseType
|
||||
}: FetchConfig): AxiosPromise {
|
||||
return request({
|
||||
url: url,
|
||||
method,
|
||||
params: appStore.getRequestTime ? { time: new Date().getTime(), ...(params || {}) } : params,
|
||||
data,
|
||||
responseType: responseType,
|
||||
headers: {
|
||||
'Content-Type': headersType || default_headers
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default fetch
|
||||
@@ -1,70 +0,0 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
|
||||
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import qs from 'qs'
|
||||
|
||||
import config from './config'
|
||||
|
||||
const { result_code, base_url } = config
|
||||
|
||||
export const PATH_URL = base_url[import.meta.env.VITE_API_BASEPATH as string]
|
||||
|
||||
// 创建axios实例
|
||||
const service: AxiosInstance = axios.create({
|
||||
baseURL: PATH_URL, // api 的 base_url
|
||||
timeout: config.request_timeout // 请求超时时间
|
||||
})
|
||||
|
||||
// request拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
if (
|
||||
config.method === 'post' &&
|
||||
(config.headers as any)['Content-Type'] === 'application/x-www-form-urlencoded'
|
||||
) {
|
||||
config.data = qs.stringify(config.data)
|
||||
}
|
||||
// get参数编码
|
||||
if (config.method === 'get' && config.params) {
|
||||
let url = config.url as string
|
||||
url += '?'
|
||||
const keys = Object.keys(config.params)
|
||||
for (const key of keys) {
|
||||
if (config.params[key] !== void 0 && config.params[key] !== null) {
|
||||
url += `${key}=${encodeURIComponent(config.params[key])}&`
|
||||
}
|
||||
}
|
||||
url = url.substring(0, url.length - 1)
|
||||
config.params = {}
|
||||
config.url = url
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
// Do something with request error
|
||||
console.log(error) // for debug
|
||||
Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// response 拦截器
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse<any>) => {
|
||||
if (response.config.responseType === 'blob') {
|
||||
// 如果是文件流,直接过
|
||||
return response
|
||||
} else if (response.data.code === result_code) {
|
||||
return response.data
|
||||
} else {
|
||||
ElMessage.error(response.data.message)
|
||||
}
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log('err' + error) // for debug
|
||||
ElMessage.error(error.message)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
||||
@@ -1,141 +0,0 @@
|
||||
<template>
|
||||
<div class="avatars-wrap">
|
||||
<template v-if="tooltip">
|
||||
<el-tooltip
|
||||
v-for="(item, $index) in avatarsData"
|
||||
:key="$index"
|
||||
:content="item.text"
|
||||
placement="top"
|
||||
>
|
||||
<div
|
||||
:class="
|
||||
showAvatar ? 'avatars-item-img' : ['avatars-item', `avatars-${item.type || 'default'}`]
|
||||
"
|
||||
>
|
||||
<el-avatar v-if="showAvatar" :size="40" :src="item.url">
|
||||
<img :src="defaultImg" />
|
||||
</el-avatar>
|
||||
<span v-else>{{ item.text.substr(0, 1) }}</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<div v-if="max && data.length - max > 0" :class="['avatars-item', 'avatars-item-img']">
|
||||
<span>+{{ data.length - max }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="(item, $index) in avatarsData"
|
||||
:key="$index"
|
||||
:class="
|
||||
showAvatar ? 'avatars-item-img' : ['avatars-item', `avatars-${item.type || 'default'}`]
|
||||
"
|
||||
>
|
||||
<el-avatar v-if="showAvatar" :size="40" :src="item.url">
|
||||
<img :src="defaultImg" />
|
||||
</el-avatar>
|
||||
<span v-else>{{ item.text.substr(0, 1) }}</span>
|
||||
</div>
|
||||
<div v-if="max && data.length - max > 0" :class="['avatars-item', 'avatars-item-img']">
|
||||
<span>+{{ data.length - max }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Avatars">
|
||||
import { PropType, computed } from 'vue'
|
||||
import { deepClone } from '@/utils'
|
||||
import { AvatarConfig } from './types'
|
||||
import defaultImg from '@/assets/img/default-avatar.png'
|
||||
|
||||
const props = defineProps({
|
||||
// 展示的数据
|
||||
data: {
|
||||
type: Array as PropType<AvatarConfig[]>,
|
||||
default: () => []
|
||||
},
|
||||
// 最大展示数量
|
||||
max: {
|
||||
type: Number as PropType<number>,
|
||||
default: 0
|
||||
},
|
||||
// 是否使用头像
|
||||
showAvatar: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
// 是否显示完整名称
|
||||
tooltip: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const avatarsData = computed(() => {
|
||||
if (props.max) {
|
||||
if (props.data.length <= props.max) {
|
||||
return props.data
|
||||
} else {
|
||||
const data = deepClone(props.data).splice(0, props.max)
|
||||
return data
|
||||
}
|
||||
} else {
|
||||
return props.data
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.avatars-wrap {
|
||||
display: flex;
|
||||
|
||||
.avatars-item {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
background: #2d8cf0;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatars-item-img {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
|
||||
.el-avatar--circle {
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.avatars-item-img + .avatars-item-img {
|
||||
margin-left: -12px;
|
||||
}
|
||||
|
||||
.avatars-item + .avatars-item {
|
||||
margin-left: -12px;
|
||||
}
|
||||
|
||||
.avatars-default {
|
||||
color: #bae7ff;
|
||||
background: #096dd9;
|
||||
}
|
||||
|
||||
.avatars-success {
|
||||
color: #f6ffed;
|
||||
background: #52c41a;
|
||||
}
|
||||
|
||||
.avatars-danger {
|
||||
color: #fff1f0;
|
||||
background: #f5222d;
|
||||
}
|
||||
|
||||
.avatars-warning {
|
||||
color: #fffbe6;
|
||||
background: #faad14;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +0,0 @@
|
||||
export interface AvatarConfig {
|
||||
text: string
|
||||
type?: string
|
||||
url?: string
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
<template>
|
||||
<span>
|
||||
{{ displayValue }}
|
||||
</span>
|
||||
</template>
|
||||
<script setup lang="ts" name="CountTo">
|
||||
import { reactive, computed, watch, onMounted, unref, toRef } from 'vue'
|
||||
import { countToProps } from './props'
|
||||
import { isNumber } from '@/utils/validate'
|
||||
|
||||
const props = defineProps(countToProps)
|
||||
|
||||
const emit = defineEmits(['mounted', 'callback'])
|
||||
|
||||
defineExpose({
|
||||
pauseResume,
|
||||
reset,
|
||||
start,
|
||||
pause
|
||||
})
|
||||
|
||||
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()
|
||||
}
|
||||
})
|
||||
|
||||
function start() {
|
||||
const { startVal, duration } = props
|
||||
state.localStartVal = startVal
|
||||
state.startTime = null
|
||||
state.localDuration = duration
|
||||
state.paused = false
|
||||
state.rAF = requestAnimationFrame(count)
|
||||
}
|
||||
|
||||
function pauseResume() {
|
||||
if (state.paused) {
|
||||
resume()
|
||||
state.paused = false
|
||||
} else {
|
||||
pause()
|
||||
state.paused = true
|
||||
}
|
||||
}
|
||||
|
||||
function pause() {
|
||||
cancelAnimationFrame(state.rAF)
|
||||
}
|
||||
|
||||
function resume() {
|
||||
state.startTime = null
|
||||
state.localDuration = +(state.remaining as number)
|
||||
state.localStartVal = +(state.printVal as number)
|
||||
requestAnimationFrame(count)
|
||||
}
|
||||
|
||||
function reset() {
|
||||
state.startTime = null
|
||||
cancelAnimationFrame(state.rAF)
|
||||
state.displayValue = formatNumber(props.startVal)
|
||||
}
|
||||
|
||||
function 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')
|
||||
}
|
||||
}
|
||||
|
||||
function 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
|
||||
}
|
||||
</script>
|
||||
@@ -1,62 +0,0 @@
|
||||
import { PropType } from 'vue'
|
||||
export const countToProps = {
|
||||
startVal: {
|
||||
type: Number as PropType<number>,
|
||||
required: false,
|
||||
default: 0
|
||||
},
|
||||
endVal: {
|
||||
type: Number as PropType<number>,
|
||||
required: false,
|
||||
default: 2017
|
||||
},
|
||||
duration: {
|
||||
type: Number as PropType<number>,
|
||||
required: false,
|
||||
default: 3000
|
||||
},
|
||||
autoplay: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
decimals: {
|
||||
type: Number as PropType<number>,
|
||||
required: false,
|
||||
default: 0,
|
||||
validator(value: number) {
|
||||
return value >= 0
|
||||
}
|
||||
},
|
||||
decimal: {
|
||||
type: String as PropType<string>,
|
||||
required: false,
|
||||
default: '.'
|
||||
},
|
||||
separator: {
|
||||
type: String as PropType<string>,
|
||||
required: false,
|
||||
default: ','
|
||||
},
|
||||
prefix: {
|
||||
type: String as PropType<string>,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
suffix: {
|
||||
type: String as PropType<string>,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
useEasing: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
required: false,
|
||||
default: 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
<template>
|
||||
<div ref="editorRef"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Editor">
|
||||
import { PropType, watch, computed, onMounted, onBeforeUnmount, ref, unref } from 'vue'
|
||||
import E from 'wangeditor'
|
||||
import hljs from 'highlight.js' // 这个蠢货插件,9以上的版本都不支持IE。辣鸡!!
|
||||
import 'highlight.js/styles/monokai-sublime.css'
|
||||
import { oneOf } from '@/utils'
|
||||
import { EditorConfig } from './types'
|
||||
import { Message } from '_c/Message'
|
||||
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object as PropType<EditorConfig>,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
valueType: {
|
||||
type: String as PropType<'html' | 'text'>,
|
||||
default: 'html',
|
||||
validator: (val: string) => {
|
||||
return oneOf(val, ['html', 'text'])
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change', 'focus', 'blur'])
|
||||
|
||||
defineExpose({
|
||||
getHtml,
|
||||
getJSON,
|
||||
getText
|
||||
})
|
||||
|
||||
let editor: Nullable<E> = null
|
||||
const value = computed(() => props.value)
|
||||
const editorRef = ref<Nullable<HTMLElement>>(null)
|
||||
|
||||
watch(
|
||||
value,
|
||||
(val: string) => {
|
||||
if (editor) {
|
||||
editor.txt.html(val)
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
function createdEditor() {
|
||||
editor = new E(unref(editorRef.value) as HTMLElement)
|
||||
initConfig()
|
||||
editor.create()
|
||||
editor.txt.html(value.value)
|
||||
}
|
||||
|
||||
function initConfig() {
|
||||
const config = props.config as EditorConfig
|
||||
const editorRef = editor as E
|
||||
|
||||
// // 设置编辑区域高度为 500px
|
||||
editorRef.config.height = config.height || 500
|
||||
|
||||
// // 设置zIndex
|
||||
editorRef.config.zIndex = config.zIndex || 0
|
||||
|
||||
// // 设置 placeholder 提示文字
|
||||
editorRef.config.placeholder = config.placeholder || '请输入文本'
|
||||
|
||||
// // 设置是否自动聚焦
|
||||
editorRef.config.focus = config.focus || false
|
||||
|
||||
// 配置菜单
|
||||
editorRef.config.menus = config.menus || [
|
||||
'head',
|
||||
'bold',
|
||||
'fontSize',
|
||||
'fontName',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikeThrough',
|
||||
'indent',
|
||||
'lineHeight',
|
||||
'foreColor',
|
||||
'backColor',
|
||||
'link',
|
||||
'list',
|
||||
'justify',
|
||||
'quote',
|
||||
'emoticon',
|
||||
'image',
|
||||
'video',
|
||||
'table',
|
||||
'code',
|
||||
'splitLine',
|
||||
'undo',
|
||||
'redo'
|
||||
]
|
||||
|
||||
// 配置颜色(文字颜色、背景色)
|
||||
editorRef.config.colors = config.colors || ['#000000', '#eeece0', '#1c487f', '#4d80bf']
|
||||
|
||||
// 配置字体
|
||||
editorRef.config.fontNames = config.fontNames || [
|
||||
'黑体',
|
||||
'仿宋',
|
||||
'楷体',
|
||||
'标楷体',
|
||||
'华文仿宋',
|
||||
'华文楷体',
|
||||
'宋体',
|
||||
'微软雅黑',
|
||||
'Arial',
|
||||
'Tahoma',
|
||||
'Verdana',
|
||||
'Times New Roman',
|
||||
'Courier New'
|
||||
]
|
||||
|
||||
// 配置行高
|
||||
editorRef.config.lineHeights = config.lineHeights || ['1', '1.15', '1.6', '2', '2.5', '3']
|
||||
|
||||
// // 代码高亮
|
||||
editorRef.highlight = hljs
|
||||
|
||||
// // 配置全屏
|
||||
editorRef.config.showFullScreen = config.showFullScreen || true
|
||||
|
||||
// 编辑器 customAlert 是对全局的alert做了统一处理,默认为 window.alert。
|
||||
// 如觉得浏览器自带的alert体验不佳,可自定义 alert,以便于达到与自身项目统一的alert效果。
|
||||
editorRef.config.customAlert =
|
||||
config.customAlert ||
|
||||
function (s: string, t: string) {
|
||||
switch (t) {
|
||||
case 'success':
|
||||
Message.success(s)
|
||||
break
|
||||
case 'info':
|
||||
Message.info(s)
|
||||
break
|
||||
case 'warning':
|
||||
Message.warning(s)
|
||||
break
|
||||
case 'error':
|
||||
Message.error(s)
|
||||
break
|
||||
default:
|
||||
Message.info(s)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 图片上传默认使用base64
|
||||
editorRef.config.uploadImgShowBase64 = true
|
||||
|
||||
// 配置 onchange 回调函数
|
||||
editorRef.config.onchange = (html: string) => {
|
||||
const text = editorRef.txt.text()
|
||||
emitFun(editor, props.valueType === 'html' ? html : text, 'change')
|
||||
}
|
||||
// 配置触发 onchange 的时间频率,默认为 200ms
|
||||
editorRef.config.onchangeTimeout = config.onchangeTimeout || 1000
|
||||
|
||||
// 编辑区域 focus(聚焦)和 blur(失焦)时触发的回调函数。
|
||||
editorRef.config.onblur = (html: string) => {
|
||||
emitFun(editor, html, 'blur')
|
||||
}
|
||||
editorRef.config.onfocus = (html: string) => {
|
||||
emitFun(editor, html, 'focus')
|
||||
}
|
||||
}
|
||||
|
||||
function emitFun(editor: any, _: string, type: 'change' | 'focus' | 'blur'): void {
|
||||
if (editor) {
|
||||
emit(type, props.valueType === 'html' ? (editor as E).txt.html() : (editor as E).txt.text())
|
||||
}
|
||||
}
|
||||
|
||||
function getHtml() {
|
||||
if (editor) {
|
||||
return (editor as E).txt.html()
|
||||
}
|
||||
}
|
||||
|
||||
function getText() {
|
||||
if (editor) {
|
||||
return (editor as E).txt.text()
|
||||
}
|
||||
}
|
||||
|
||||
function getJSON() {
|
||||
if (editor) {
|
||||
return (editor as E).txt.getJSON()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
createdEditor()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (editor) {
|
||||
;(editor as E).destroy()
|
||||
editor = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,13 +0,0 @@
|
||||
export interface EditorConfig {
|
||||
height?: number // 富文本高度
|
||||
zIndex?: number // 层级
|
||||
placeholder?: string // 提示文字
|
||||
focus?: boolean // 是否聚焦
|
||||
onchangeTimeout?: number // 几秒监听一次变化
|
||||
customAlert?: (s: string, t: string) => {} // 自定义提示
|
||||
menus?: string[] // 按钮菜单
|
||||
colors?: string[] // 颜色
|
||||
fontNames?: string[] // 字体
|
||||
lineHeights?: string[] // 行间距
|
||||
showFullScreen?: boolean // 是否全屏
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import { defineComponent, PropType, computed, h } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'Highlight',
|
||||
props: {
|
||||
tag: {
|
||||
type: String as PropType<string>,
|
||||
default: 'span'
|
||||
},
|
||||
keys: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
color: {
|
||||
type: String as PropType<string>,
|
||||
default: '#2d8cf0'
|
||||
}
|
||||
},
|
||||
emits: ['click'],
|
||||
setup(props, { emit }) {
|
||||
const keyNodes = computed(() => {
|
||||
return props.keys.map((key) => {
|
||||
return h(
|
||||
'span',
|
||||
{
|
||||
onClick: () => {
|
||||
emit('click', key)
|
||||
},
|
||||
style: {
|
||||
color: props.color,
|
||||
cursor: 'pointer'
|
||||
}
|
||||
},
|
||||
key
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
function parseText(text: string) {
|
||||
props.keys.forEach((key, index) => {
|
||||
const regexp = new RegExp(key, 'g')
|
||||
text = text.replace(regexp, `{{${index}}}`)
|
||||
})
|
||||
return text.split(/{{|}}/)
|
||||
}
|
||||
|
||||
return {
|
||||
keyNodes,
|
||||
parseText
|
||||
}
|
||||
},
|
||||
render(props: any) {
|
||||
if (!props.$slots.default) return null
|
||||
const node = props.$slots.default()[0].children
|
||||
if (!node) {
|
||||
console.warn('Highlight组件的插槽必须要是文本')
|
||||
return props.$slots.default()[0]
|
||||
}
|
||||
const textArray = props.parseText(node)
|
||||
const regexp = /^[0-9]*$/
|
||||
const nodes = textArray.map((t: any) => {
|
||||
if (regexp.test(t)) {
|
||||
return props.keyNodes[Math.floor(t)] || t
|
||||
}
|
||||
return t
|
||||
})
|
||||
return h(props.tag, nodes)
|
||||
}
|
||||
})
|
||||
@@ -1,33 +0,0 @@
|
||||
import ImgPreview from './index.vue'
|
||||
import { isClient } from '@/utils/validate'
|
||||
|
||||
import type { Options, Props } from './types'
|
||||
|
||||
import { createVNode, render } from 'vue'
|
||||
|
||||
let instance: any = null
|
||||
|
||||
export function createImgPreview(options: Options) {
|
||||
if (!isClient) return
|
||||
const {
|
||||
imageList,
|
||||
show = true,
|
||||
index = 0,
|
||||
onSelect = null,
|
||||
onClose = null,
|
||||
zIndex = 500
|
||||
} = options
|
||||
|
||||
const propsData: Partial<Props> = {}
|
||||
const container = document.createElement('div')
|
||||
propsData.imageList = imageList
|
||||
propsData.show = show
|
||||
propsData.index = index
|
||||
propsData.zIndex = zIndex
|
||||
propsData.onSelect = onSelect
|
||||
propsData.onClose = onClose
|
||||
|
||||
document.body.appendChild(container)
|
||||
instance = createVNode(ImgPreview, propsData)
|
||||
render(instance, container)
|
||||
}
|
||||
@@ -1,429 +0,0 @@
|
||||
<template>
|
||||
<transition name="viewer-fade">
|
||||
<div
|
||||
v-show="show"
|
||||
ref="wrapElRef"
|
||||
tabindex="-1"
|
||||
:style="{ 'z-index': zIndex }"
|
||||
class="image-viewer__wrapper"
|
||||
>
|
||||
<div class="image-viewer__mask"></div>
|
||||
<!-- CLOSE -->
|
||||
<span class="image-viewer__btn image-viewer__close" @click="hide">
|
||||
<i class="el-icon-circle-close iconfont"></i>
|
||||
</span>
|
||||
<!-- ARROW -->
|
||||
<template v-if="!isSingle">
|
||||
<span
|
||||
class="image-viewer__btn image-viewer__prev"
|
||||
:class="{ 'is-disabled': !infinite && isFirst }"
|
||||
@click="prev"
|
||||
>
|
||||
<i class="el-icon-arrow-left iconfont"></i>
|
||||
</span>
|
||||
<span
|
||||
class="image-viewer__btn image-viewer__next"
|
||||
:class="{ 'is-disabled': !infinite && isLast }"
|
||||
@click="next"
|
||||
>
|
||||
<i class="el-icon-arrow-right iconfont"></i>
|
||||
</span>
|
||||
</template>
|
||||
<!-- ACTIONS -->
|
||||
<div class="image-viewer__btn image-viewer__actions">
|
||||
<div class="image-viewer__actions__inner">
|
||||
<svg-icon class="iconfont" icon-class="unscale" @click="handleActions('zoomOut')" />
|
||||
<svg-icon class="iconfont" icon-class="scale" @click="handleActions('zoomIn')" />
|
||||
<svg-icon class="iconfont" icon-class="resume" @click="toggleMode" />
|
||||
<svg-icon
|
||||
class="iconfont"
|
||||
icon-class="unrotate"
|
||||
@click="handleActions('anticlocelise')"
|
||||
/>
|
||||
<svg-icon class="iconfont" icon-class="rotate" @click="handleActions('clocelise')" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- CANVAS -->
|
||||
<div class="image-viewer__canvas">
|
||||
<img
|
||||
ref="imgRef"
|
||||
:src="currentImg"
|
||||
:style="imgStyle"
|
||||
class="image-viewer__img"
|
||||
@load="handleImgLoad"
|
||||
@error="handleImgError"
|
||||
@mousedown="handleMouseDown"
|
||||
@click="select"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Preview">
|
||||
import { ref, reactive, computed, watch, nextTick, unref } from 'vue'
|
||||
import { previewProps } from './props'
|
||||
import { isFirefox } from '@/utils/validate'
|
||||
import { on, off } from '@/utils/dom-utils'
|
||||
import { throttle } from 'lodash-es'
|
||||
import SvgIcon from '_c/SvgIcon/index.vue'
|
||||
const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel'
|
||||
|
||||
const props = defineProps(previewProps)
|
||||
|
||||
const infinite = ref<boolean>(true)
|
||||
const loading = ref<boolean>(false)
|
||||
const show = ref<boolean>(props.show)
|
||||
const index = ref<number>(props.index)
|
||||
const transform = reactive({
|
||||
scale: 1,
|
||||
deg: 0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
enableTransition: false
|
||||
})
|
||||
const isSingle = computed((): boolean => props.imageList.length <= 1)
|
||||
const isFirst = computed((): boolean => index.value === 0)
|
||||
const isLast = computed((): boolean => index.value === props.imageList.length - 1)
|
||||
const currentImg = computed((): string => props.imageList[index.value])
|
||||
const imgStyle = computed(() => {
|
||||
const { scale, deg, offsetX, offsetY, enableTransition } = transform
|
||||
const style = {
|
||||
transform: `scale(${scale}) rotate(${deg}deg)`,
|
||||
transition: enableTransition ? 'transform .3s' : '',
|
||||
'margin-left': `${offsetX}px`,
|
||||
'margin-top': `${offsetY}px`
|
||||
}
|
||||
return style
|
||||
})
|
||||
|
||||
const wrapElRef = ref<HTMLElement | null>(null)
|
||||
const imgRef = ref<HTMLElement | null>(null)
|
||||
|
||||
let _keyDownHandler: Function | null = null
|
||||
let _mouseWheelHandler: Function | null = null
|
||||
let _dragHandler: Function | null = null
|
||||
|
||||
watch(
|
||||
() => index.value,
|
||||
() => {
|
||||
reset()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => currentImg.value,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
const $img = unref(imgRef) as any
|
||||
if (!$img.complete) {
|
||||
loading.value = true
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => show.value,
|
||||
(show: boolean) => {
|
||||
if (show) {
|
||||
nextTick(() => {
|
||||
;(unref(wrapElRef) as any).focus()
|
||||
document.body.style.overflow = 'hidden'
|
||||
deviceSupportInstall()
|
||||
})
|
||||
} else {
|
||||
nextTick(() => {
|
||||
document.body.style.overflow = 'auto'
|
||||
deviceSupportUninstall()
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
function hide(): void {
|
||||
show.value = false
|
||||
if (typeof props.onClose === 'function') {
|
||||
props.onClose(index.value)
|
||||
}
|
||||
}
|
||||
|
||||
function select(): void {
|
||||
if (typeof props.onSelect === 'function') {
|
||||
props.onSelect(index.value)
|
||||
}
|
||||
}
|
||||
|
||||
function deviceSupportInstall(): void {
|
||||
_keyDownHandler = throttle((e: any) => {
|
||||
const keyCode = e.keyCode
|
||||
switch (keyCode) {
|
||||
// ESC
|
||||
case 27:
|
||||
hide()
|
||||
break
|
||||
// SPACE
|
||||
case 32:
|
||||
toggleMode()
|
||||
break
|
||||
// LEFT_ARROW
|
||||
case 37:
|
||||
prev()
|
||||
break
|
||||
// UP_ARROW
|
||||
case 38:
|
||||
handleActions('zoomIn')
|
||||
break
|
||||
// RIGHT_ARROW
|
||||
case 39:
|
||||
next()
|
||||
break
|
||||
// DOWN_ARROW
|
||||
case 40:
|
||||
handleActions('zoomOut')
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
_mouseWheelHandler = throttle((e: any) => {
|
||||
const delta = e.wheelDelta ? e.wheelDelta : -e.detail
|
||||
if (delta > 0) {
|
||||
handleActions('zoomIn', {
|
||||
zoomRate: 0.015,
|
||||
enableTransition: false
|
||||
})
|
||||
} else {
|
||||
handleActions('zoomOut', {
|
||||
zoomRate: 0.015,
|
||||
enableTransition: false
|
||||
})
|
||||
}
|
||||
})
|
||||
on(document, 'keydown', _keyDownHandler as any)
|
||||
on(document, mousewheelEventName, _mouseWheelHandler as any)
|
||||
}
|
||||
|
||||
function deviceSupportUninstall(): void {
|
||||
off(document, 'keydown', _keyDownHandler)
|
||||
off(document, mousewheelEventName, _mouseWheelHandler)
|
||||
_keyDownHandler = null
|
||||
_mouseWheelHandler = null
|
||||
}
|
||||
|
||||
function handleImgLoad(): void {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
function handleImgError(e: any): void {
|
||||
loading.value = false
|
||||
e.target.alt = '加载失败'
|
||||
}
|
||||
|
||||
function handleMouseDown(e: any): void {
|
||||
if (loading.value || e.button !== 0) return
|
||||
const { offsetX, offsetY } = transform
|
||||
const startX = e.pageX
|
||||
const startY = e.pageY
|
||||
_dragHandler = throttle((ev: any) => {
|
||||
transform.offsetX = offsetX + ev.pageX - startX
|
||||
transform.offsetY = offsetY + ev.pageY - startY
|
||||
})
|
||||
on(document, 'mousemove', _dragHandler as any)
|
||||
on(document, 'mouseup', () => {
|
||||
off(document, 'mousemove', _dragHandler as any)
|
||||
})
|
||||
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
transform.scale = 1
|
||||
transform.deg = 0
|
||||
transform.offsetX = 0
|
||||
transform.offsetY = 0
|
||||
transform.enableTransition = false
|
||||
}
|
||||
|
||||
function toggleMode(): void {
|
||||
if (loading.value) return
|
||||
reset()
|
||||
}
|
||||
|
||||
function prev(): void {
|
||||
if (isFirst.value && !infinite.value) return
|
||||
const len = props.imageList.length
|
||||
index.value = (index.value - 1 + len) % len
|
||||
}
|
||||
|
||||
function next(): void {
|
||||
if (isLast.value && !infinite.value) return
|
||||
const len = props.imageList.length
|
||||
index.value = (index.value + 1) % len
|
||||
}
|
||||
|
||||
function handleActions(action: string, options: any = {}): void {
|
||||
if (loading.value) return
|
||||
const style = {
|
||||
zoomRate: 0.2,
|
||||
rotateDeg: 90,
|
||||
enableTransition: true,
|
||||
...options
|
||||
}
|
||||
const { zoomRate, rotateDeg, enableTransition } = style
|
||||
switch (action) {
|
||||
case 'zoomOut':
|
||||
if (transform.scale > 0.2) {
|
||||
transform.scale = parseFloat((transform.scale - zoomRate).toFixed(3))
|
||||
}
|
||||
break
|
||||
case 'zoomIn':
|
||||
transform.scale = parseFloat((transform.scale + zoomRate).toFixed(3))
|
||||
break
|
||||
case 'clocelise':
|
||||
transform.deg += rotateDeg
|
||||
break
|
||||
case 'anticlocelise':
|
||||
transform.deg -= rotateDeg
|
||||
break
|
||||
}
|
||||
transform.enableTransition = enableTransition
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.iconfont {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.image-viewer__wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.image-viewer__btn {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
opacity: 0.8;
|
||||
box-sizing: border-box;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-viewer__close {
|
||||
top: 40px;
|
||||
right: 40px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 40px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.image-viewer__canvas {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image-viewer__actions {
|
||||
bottom: 30px;
|
||||
left: 50%;
|
||||
width: 282px;
|
||||
height: 44px;
|
||||
padding: 0 23px;
|
||||
background-color: #606266;
|
||||
border-color: #fff;
|
||||
border-radius: 22px;
|
||||
transform: translateX(-50%);
|
||||
|
||||
.image-viewer__actions__inner {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 23px;
|
||||
color: #fff;
|
||||
text-align: justify;
|
||||
cursor: default;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
}
|
||||
|
||||
.image-viewer__prev {
|
||||
top: 50%;
|
||||
left: 40px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
background-color: #606266;
|
||||
border-color: #fff;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.image-viewer__next {
|
||||
top: 50%;
|
||||
right: 40px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
text-indent: 2px;
|
||||
background-color: #606266;
|
||||
border-color: #fff;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.image-viewer__mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #000;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.viewer-fade-enter-active {
|
||||
animation: viewer-fade-in 0.3s;
|
||||
}
|
||||
|
||||
.viewer-fade-leave-active {
|
||||
animation: viewer-fade-out 0.3s;
|
||||
}
|
||||
|
||||
@keyframes viewer-fade-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, -20px, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes viewer-fade-out {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, -20px, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,28 +0,0 @@
|
||||
import { PropType } from 'vue'
|
||||
|
||||
export const previewProps = {
|
||||
index: {
|
||||
type: Number as PropType<number>,
|
||||
default: 0
|
||||
},
|
||||
zIndex: {
|
||||
type: Number as PropType<number>,
|
||||
default: 100
|
||||
},
|
||||
show: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
imageList: {
|
||||
type: [Array] as PropType<string[]>,
|
||||
default: []
|
||||
},
|
||||
onClose: {
|
||||
type: Function as PropType<Function>,
|
||||
default: null
|
||||
},
|
||||
onSelect: {
|
||||
type: Function as PropType<Function>,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
export interface Options {
|
||||
show?: boolean
|
||||
imageList: string[]
|
||||
index?: number
|
||||
zIndex?: number
|
||||
onSelect?: Function | null
|
||||
onClose?: Function | null
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
show: boolean
|
||||
instance: Props
|
||||
imageList: string[]
|
||||
index: number
|
||||
zIndex: number
|
||||
onSelect: Function | null
|
||||
onClose: Function | null
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
<template>
|
||||
<div v-loading="loading" class="qrcode__wrap" :style="wrapStyle">
|
||||
<component :is="tag" ref="wrapRef" @click="clickCode" />
|
||||
<div v-if="disabled" class="disabled__wrap" @click="disabledClick">
|
||||
<div>
|
||||
<i class="el-icon-refresh-right"></i>
|
||||
<div>{{ disabledText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Qrcode">
|
||||
import { PropType, nextTick, ref, watch, computed, unref } from 'vue'
|
||||
import type { LogoTypes } from './types'
|
||||
import QRCode from 'qrcode'
|
||||
import { QRCodeRenderersOptions } from 'qrcode'
|
||||
import { deepClone } from '@/utils'
|
||||
import { isString } from '@/utils/validate'
|
||||
const { toCanvas, toDataURL } = QRCode
|
||||
|
||||
const props = defineProps({
|
||||
// img 或者 canvas,img不支持logo嵌套
|
||||
tag: {
|
||||
type: String as PropType<'canvas' | 'img'>,
|
||||
default: 'canvas',
|
||||
validator: (v: string) => ['canvas', 'img'].includes(v)
|
||||
},
|
||||
// 二维码内容
|
||||
text: {
|
||||
type: [String, Array] as PropType<string | any[]>,
|
||||
default: null
|
||||
},
|
||||
// qrcode.js配置项
|
||||
options: {
|
||||
type: Object as PropType<QRCodeRenderersOptions>,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 宽度
|
||||
width: {
|
||||
type: Number as PropType<number>,
|
||||
default: 200
|
||||
},
|
||||
// logo
|
||||
logo: {
|
||||
type: [String, Object] as PropType<Partial<LogoTypes> | string>,
|
||||
default: ''
|
||||
},
|
||||
// 是否过期
|
||||
disabled: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
// 过期提示内容
|
||||
disabledText: {
|
||||
type: String as PropType<string>,
|
||||
default: '二维码已失效'
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['done', 'click', 'disabled-click'])
|
||||
const loading = ref<boolean>(true)
|
||||
const wrapRef = ref<HTMLCanvasElement | HTMLImageElement | null>(null)
|
||||
const renderText = computed(() => String(props.text))
|
||||
const wrapStyle = computed(() => {
|
||||
return {
|
||||
width: props.width + 'px',
|
||||
height: props.width + 'px'
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => renderText.value,
|
||||
(val) => {
|
||||
if (!val) return
|
||||
initQrcode()
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
// 初始化
|
||||
function initQrcode() {
|
||||
nextTick(async () => {
|
||||
const options = deepClone(props.options || {})
|
||||
if (props.tag === 'canvas') {
|
||||
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
|
||||
options.errorCorrectionLevel =
|
||||
options.errorCorrectionLevel || getErrorCorrectionLevel(renderText.value)
|
||||
getOriginWidth(renderText.value, options).then(async (_width) => {
|
||||
options.scale = props.width === 0 ? undefined : (props.width / _width) * 4
|
||||
const canvasRef: any = await toCanvas(unref(wrapRef as any), renderText.value, options)
|
||||
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 any).src = url
|
||||
emit('done', url)
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 生成logo
|
||||
function createLogoCode(canvasRef: HTMLCanvasElement) {
|
||||
const canvasWidth = canvasRef.width
|
||||
const logoOptions: LogoTypes = 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大小
|
||||
function getOriginWidth(content: string, options: QRCodeRenderersOptions) {
|
||||
const _canvas = document.createElement('canvas')
|
||||
return toCanvas(_canvas, content, options).then(() => _canvas.width)
|
||||
}
|
||||
|
||||
// 对于内容少的QrCode,增大容错率
|
||||
function getErrorCorrectionLevel(content: string) {
|
||||
if (content.length > 36) {
|
||||
return 'M'
|
||||
} else if (content.length > 16) {
|
||||
return 'Q'
|
||||
} else {
|
||||
return 'H'
|
||||
}
|
||||
}
|
||||
|
||||
// 点击二维码
|
||||
function clickCode() {
|
||||
emit('click')
|
||||
}
|
||||
|
||||
// 失效点击事件
|
||||
function disabledClick() {
|
||||
emit('disabled-click')
|
||||
}
|
||||
|
||||
// copy来的方法,用于绘制圆角
|
||||
function 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
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.qrcode__wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
.disabled__wrap {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
& > div {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
font-weight: bold;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
i {
|
||||
margin-bottom: 10px;
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +0,0 @@
|
||||
export interface LogoTypes {
|
||||
src?: string
|
||||
logoSize?: number
|
||||
bgColor?: string
|
||||
borderSize?: number
|
||||
crossOrigin?: string
|
||||
borderRadius?: number
|
||||
logoRadius?: number
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import Clipboard from 'clipboard'
|
||||
import { Directive, DirectiveBinding } from 'vue'
|
||||
import { Message } from '_c/Message'
|
||||
|
||||
if (!Clipboard) {
|
||||
throw new Error('you should npm install `clipboard` --save at first ')
|
||||
}
|
||||
|
||||
export const clipboard: Directive = {
|
||||
beforeMount(el: HTMLElement, binding: DirectiveBinding) {
|
||||
createdClipboard(el, binding.arg, binding.value)
|
||||
},
|
||||
updated(el: HTMLElement | any, binding: DirectiveBinding) {
|
||||
if (binding.arg === 'success') {
|
||||
el._v_clipboard_success = binding.value
|
||||
} else if (binding.arg === 'error') {
|
||||
el._v_clipboard_error = binding.value
|
||||
} else {
|
||||
el._v_clipboard.text = function () {
|
||||
return binding.value
|
||||
}
|
||||
el._v_clipboard.action = function () {
|
||||
return 'copy'
|
||||
}
|
||||
}
|
||||
},
|
||||
unmounted(el: HTMLElement | any, binding: DirectiveBinding) {
|
||||
if (binding.arg === 'success') {
|
||||
delete el._v_clipboard_success
|
||||
} else if (binding.arg === 'error') {
|
||||
delete el._v_clipboard_error
|
||||
} else {
|
||||
el._v_clipboard.destroy()
|
||||
delete el._v_clipboard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createdClipboard(el: HTMLElement | any, arg: string | undefined, value: any) {
|
||||
if (arg === 'success') {
|
||||
el._v_clipboard_success = value
|
||||
} else if (arg === 'error') {
|
||||
el._v_clipboard_error = value
|
||||
} else {
|
||||
const clipboard = new Clipboard(el, {
|
||||
text() {
|
||||
return value
|
||||
},
|
||||
action() {
|
||||
return 'copy'
|
||||
}
|
||||
})
|
||||
clipboard.on('success', (e) => {
|
||||
const callback = el._v_clipboard_success
|
||||
if (callback) {
|
||||
callback(e)
|
||||
} else {
|
||||
Message.success('复制成功')
|
||||
}
|
||||
})
|
||||
clipboard.on('error', (e) => {
|
||||
const callback = el._v_clipboard_error
|
||||
if (callback) {
|
||||
callback(e)
|
||||
} else {
|
||||
Message.success('复制失败')
|
||||
}
|
||||
})
|
||||
el._v_clipboard = clipboard
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { App } from 'vue'
|
||||
|
||||
import { clipboard } from './clipboard'
|
||||
|
||||
export function setupDirectives(app: App) {
|
||||
app.directive('clipboard', clipboard)
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
import { reactive, ref } from 'vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { Message } from '_c/Message'
|
||||
|
||||
interface DefalutParams {
|
||||
pageIndex: number // 页码
|
||||
pageSize: number // 页数
|
||||
}
|
||||
|
||||
interface DelsParmas {
|
||||
noDataText?: string // 没有选中数据时的提示
|
||||
text?: string // 删除前的提示
|
||||
hiddenVerify?: boolean // 是否隐藏前置判断
|
||||
}
|
||||
|
||||
interface InitOption {
|
||||
listFun?: Fn
|
||||
delFun?: Fn
|
||||
}
|
||||
|
||||
interface DilogOption {
|
||||
title?: string
|
||||
width?: string
|
||||
}
|
||||
|
||||
export function useWork(option?: InitOption) {
|
||||
const listFun: Nullable<Fn> = option?.listFun || null
|
||||
|
||||
const delFun: Nullable<Fn> = option?.delFun || null
|
||||
|
||||
// 请求接口的基本参数
|
||||
const defaultParams = reactive<DefalutParams>({
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
// 多选数据
|
||||
const selectionData = ref<IObj>([])
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref<IObj>([])
|
||||
|
||||
// 表格加载状态
|
||||
const loading = ref<boolean>(true)
|
||||
|
||||
// 表格总条数
|
||||
const total = ref<number>(0)
|
||||
|
||||
// 是否展示弹窗
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
// 弹窗标题
|
||||
const dialogTitle = ref<string>('')
|
||||
|
||||
// 组件名称
|
||||
const comName = ref<string>('')
|
||||
|
||||
// 弹窗宽度
|
||||
const dialogWidth = ref<string>('')
|
||||
|
||||
// 传送的数据
|
||||
const rowData = ref<Nullable<IObj>>(null)
|
||||
|
||||
// 需要传给后端的其他参数
|
||||
const otherParams = ref<Nullable<IObj>>({})
|
||||
|
||||
// 表格展示条目改变时候重置基本参数
|
||||
function sizeChange(val: number) {
|
||||
loading.value = true
|
||||
defaultParams.pageIndex = 1
|
||||
defaultParams.pageSize = val
|
||||
}
|
||||
|
||||
// 表格分页改变时候重置基本参数
|
||||
function currentChange(val: number) {
|
||||
loading.value = true
|
||||
defaultParams.pageIndex = val
|
||||
}
|
||||
|
||||
// 删除多选
|
||||
/**
|
||||
* @param {Object} callBack
|
||||
* @param {Object} config {
|
||||
noDataText?: string // 没有选中数据时的提示
|
||||
text?: string // 删除前的提示
|
||||
hiddenVerify?: boolean // 是否隐藏前置判断
|
||||
}
|
||||
*/
|
||||
function delData(callBack: Fn, config: DelsParmas) {
|
||||
if (!selectionData.value.length && config && !config.hiddenVerify) {
|
||||
Message.warning((config && config.noDataText) || '请选择需要删除的数据!')
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm((config && config.text) || '此操作将永久删除选中数据, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
await callBack()
|
||||
})
|
||||
}
|
||||
|
||||
// 多选变化的时候
|
||||
function handleSelectionChange(selection: IObj) {
|
||||
selectionData.value = selection
|
||||
}
|
||||
|
||||
// 改变弹窗dialogVisible
|
||||
function toggleVisible(val = false) {
|
||||
dialogVisible.value = val
|
||||
}
|
||||
|
||||
// 以下都是业务逻辑混入
|
||||
// 请求数据
|
||||
async function getList(data?: IObj) {
|
||||
try {
|
||||
const res = await (listFun as Fn)({
|
||||
params: Object.assign(otherParams.value, data || {}, defaultParams)
|
||||
})
|
||||
if (res) {
|
||||
// 返回结果可以自己更改
|
||||
console.log(res)
|
||||
total.value = res.data.total
|
||||
tableData.value = res.data.list
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 查询
|
||||
function searchSubmit(data: IObj) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(1)
|
||||
getList(data)
|
||||
}
|
||||
|
||||
// 重置
|
||||
function resetSubmit(data: IObj) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(1)
|
||||
getList(data)
|
||||
}
|
||||
|
||||
// 展示多少条
|
||||
function handleSizeChange(val: number) {
|
||||
// 该方法重置了一些默认参数
|
||||
sizeChange(val)
|
||||
getList()
|
||||
}
|
||||
|
||||
// 展示第几页
|
||||
function handleCurrentChange(val: number) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(val)
|
||||
getList()
|
||||
}
|
||||
|
||||
// 删除多选
|
||||
function dels(item: IObj, single?: boolean, callback?: Fn) {
|
||||
delData(
|
||||
async () => {
|
||||
let ids: Nullable<IObj[]> = null
|
||||
if (item.id) {
|
||||
ids = single ? item.id : [item.id]
|
||||
} else {
|
||||
ids = selectionData.value.map((v: IObj) => {
|
||||
return v.id
|
||||
})
|
||||
}
|
||||
const res = await (delFun as Fn)({
|
||||
data: { ids }
|
||||
})
|
||||
if (res) {
|
||||
Message.success('删除成功')
|
||||
const pageIndex =
|
||||
total.value % defaultParams.pageSize === (ids as IObj[]).length ||
|
||||
defaultParams.pageSize === 1
|
||||
? defaultParams.pageIndex > 1
|
||||
? defaultParams.pageIndex - 1
|
||||
: defaultParams.pageIndex
|
||||
: defaultParams.pageIndex
|
||||
currentChange(pageIndex)
|
||||
getList()
|
||||
callback && callback instanceof Function && callback()
|
||||
}
|
||||
},
|
||||
{ hiddenVerify: item.id }
|
||||
)
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
function open(row: Nullable<IObj>, component: string, options?: DilogOption) {
|
||||
comName.value = component
|
||||
dialogTitle.value =
|
||||
(options && options.title) || (!row ? '新增' : component === 'Detail' ? '详情' : '编辑')
|
||||
dialogWidth.value = (options && options.width) || '55%'
|
||||
rowData.value = row || null
|
||||
toggleVisible(true)
|
||||
}
|
||||
|
||||
// 成功之后的回调
|
||||
function refreshTable(type: string) {
|
||||
if (type === 'add') {
|
||||
currentChange(1)
|
||||
}
|
||||
toggleVisible()
|
||||
getList()
|
||||
}
|
||||
|
||||
return {
|
||||
defaultParams,
|
||||
selectionData,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
dialogVisible,
|
||||
dialogTitle,
|
||||
comName,
|
||||
dialogWidth,
|
||||
rowData,
|
||||
otherParams,
|
||||
// delData,
|
||||
handleSelectionChange,
|
||||
toggleVisible,
|
||||
getList,
|
||||
searchSubmit,
|
||||
resetSubmit,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
dels,
|
||||
open,
|
||||
refreshTable
|
||||
}
|
||||
}
|
||||
11
src/main.ts
11
src/main.ts
@@ -6,31 +6,20 @@ import router, { setupRouter } from './router' // 路由
|
||||
|
||||
import { setupStore } from './store' // 状态管理
|
||||
|
||||
import { setupDirectives } from '@/directives' // 自定义指令
|
||||
|
||||
import { setupGlobCom } from './components'
|
||||
|
||||
// import { setupElement } from '@/plugins/element-plus'
|
||||
|
||||
import '@/styles/index.less'
|
||||
|
||||
import 'virtual:svg-icons-register'
|
||||
|
||||
import './permission' // permission control
|
||||
|
||||
import { mockXHR } from '@/mock'
|
||||
mockXHR()
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
setupStore(app) // 引入状态管理
|
||||
|
||||
setupRouter(app) // 引入路由
|
||||
|
||||
setupDirectives(app)
|
||||
|
||||
// setupElement(app)
|
||||
|
||||
setupGlobCom(app) // 引入全局组件
|
||||
|
||||
router.isReady().then(() => {
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
import Mock from 'mockjs'
|
||||
import { toAnyString } from '@/utils'
|
||||
|
||||
let List: any[] = []
|
||||
const count = 100
|
||||
|
||||
const baseContent =
|
||||
'<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
List.push(
|
||||
Mock.mock({
|
||||
id: toAnyString(),
|
||||
// timestamp: +Mock.Random.date('T'),
|
||||
author: '@first',
|
||||
title: '@title(5, 10)',
|
||||
content: baseContent,
|
||||
importance: '@integer(1, 3)',
|
||||
display_time: '@datetime',
|
||||
pageviews: '@integer(300, 5000)'
|
||||
// image_uri
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export default [
|
||||
// 列表接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/example/list',
|
||||
type: 'get',
|
||||
response: (config: any) => {
|
||||
const { title, pageIndex, pageSize } = config.query
|
||||
|
||||
const mockList = List.filter((item) => {
|
||||
if (title && item.title.indexOf(title) < 0) return false
|
||||
return true
|
||||
})
|
||||
const pageList = mockList.filter(
|
||||
(_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1)
|
||||
)
|
||||
|
||||
return {
|
||||
code: '0000',
|
||||
data: {
|
||||
total: mockList.length,
|
||||
list: pageList
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 删除接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/example/delete',
|
||||
type: 'post',
|
||||
response: (config: any) => {
|
||||
const ids = config.body.ids
|
||||
if (!ids) {
|
||||
return {
|
||||
code: '500',
|
||||
message: '请选择需要删除的数据'
|
||||
}
|
||||
} else {
|
||||
let i = List.length
|
||||
while (i--) {
|
||||
if (ids.indexOf(List[i].id) !== -1) {
|
||||
List.splice(i, 1)
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: '0000',
|
||||
data: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 详情接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/example/detail',
|
||||
type: 'get',
|
||||
response: (config: any) => {
|
||||
const { id } = config.query
|
||||
for (const example of List) {
|
||||
if (example.id === id) {
|
||||
return {
|
||||
code: '0000',
|
||||
data: example
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 保存接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/example/save',
|
||||
type: 'post',
|
||||
response: (config: any) => {
|
||||
const data = config.body
|
||||
if (!data.id) {
|
||||
List = [
|
||||
Object.assign(data, {
|
||||
id: toAnyString(),
|
||||
importance: Number(data.importance)
|
||||
})
|
||||
].concat(List)
|
||||
return {
|
||||
code: '0000',
|
||||
data: 'success'
|
||||
}
|
||||
} else {
|
||||
List.map((item) => {
|
||||
if (item.id === data.id) {
|
||||
for (const key in item) {
|
||||
if (key === 'importance') {
|
||||
item[key] = Number(data[key])
|
||||
} else {
|
||||
item[key] = data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return {
|
||||
code: '0000',
|
||||
data: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,64 +0,0 @@
|
||||
import Mock from 'mockjs'
|
||||
import { param2Obj } from '@/utils'
|
||||
|
||||
import example from './example'
|
||||
import user from './user'
|
||||
import role from './role'
|
||||
|
||||
const mocks = [...example, ...user, ...role]
|
||||
|
||||
// for front mock
|
||||
// please use it cautiously, it will redefine XMLHttpRequest,
|
||||
// which will cause many of your third-party libraries to be invalidated(like progress event).
|
||||
export function mockXHR() {
|
||||
const MockJs: any = Mock
|
||||
MockJs.XHR.prototype.proxy_send = MockJs.XHR.prototype.send
|
||||
MockJs.XHR.prototype.send = function () {
|
||||
if (this.custom.xhr) {
|
||||
this.custom.xhr.withCredentials = this.withCredentials || false
|
||||
|
||||
if (this.responseType) {
|
||||
this.custom.xhr.responseType = this.responseType
|
||||
}
|
||||
}
|
||||
/* eslint-disable */
|
||||
this.proxy_send(...arguments)
|
||||
}
|
||||
|
||||
function XHR2ExpressReqWrap(respond: any) {
|
||||
return function (options: any) {
|
||||
let result = null
|
||||
if (respond instanceof Function) {
|
||||
const { body, type, url } = options
|
||||
// https://expressjs.com/en/4x/api.html#req
|
||||
result = respond({
|
||||
method: type,
|
||||
body: JSON.parse(body),
|
||||
query: param2Obj(url)
|
||||
})
|
||||
} else {
|
||||
result = respond
|
||||
}
|
||||
return Mock.mock(result)
|
||||
}
|
||||
}
|
||||
|
||||
for (const i of mocks) {
|
||||
Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
|
||||
}
|
||||
}
|
||||
|
||||
// for mock server
|
||||
const responseFake = (url: string, type: string, respond: any) => {
|
||||
return {
|
||||
url: new RegExp(`${url}`),
|
||||
type: type || 'get',
|
||||
response(req: any, res: any) {
|
||||
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default mocks.map((route) => {
|
||||
return responseFake(route.url, route.type, route.response)
|
||||
})
|
||||
@@ -1,606 +0,0 @@
|
||||
export const checkedNodes = [
|
||||
{
|
||||
path: '/components-demo',
|
||||
title: '功能组件',
|
||||
name: 'ComponentsDemo',
|
||||
children: [
|
||||
{
|
||||
path: '/components-demo/echarts',
|
||||
title: '图表',
|
||||
name: 'EchartsDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/preview',
|
||||
title: '图片预览',
|
||||
name: 'PreviewDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/button',
|
||||
title: '按钮',
|
||||
name: 'ButtonDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/message',
|
||||
title: '消息提示',
|
||||
name: 'MessageDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/count-to',
|
||||
title: '数字动画',
|
||||
name: 'CountToDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/search',
|
||||
title: '查询',
|
||||
name: 'SearchDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/editor',
|
||||
title: '富文本编辑器',
|
||||
name: 'EditorDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/markdown',
|
||||
title: 'markdown编辑器',
|
||||
name: 'MarkdownDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/dialog',
|
||||
title: '弹窗',
|
||||
name: 'DialogDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/detail',
|
||||
title: '详情',
|
||||
name: 'DetailDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/qrcode',
|
||||
title: '二维码',
|
||||
name: 'QrcodeDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/avatars',
|
||||
title: '头像组',
|
||||
name: 'AvatarsDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/highlight',
|
||||
title: '文字高亮',
|
||||
name: 'HighlightDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/watermark',
|
||||
name: 'WatermarkDemo',
|
||||
title: '水印'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/components-demo/echarts',
|
||||
title: '图表',
|
||||
name: 'EchartsDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/preview',
|
||||
title: '图片预览',
|
||||
name: 'PreviewDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/button',
|
||||
title: '按钮',
|
||||
name: 'ButtonDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/message',
|
||||
title: '消息提示',
|
||||
name: 'MessageDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/count-to',
|
||||
title: '数字动画',
|
||||
name: 'CountToDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/search',
|
||||
title: '查询',
|
||||
name: 'SearchDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/editor',
|
||||
title: '富文本编辑器',
|
||||
name: 'EditorDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/markdown',
|
||||
title: 'markdown编辑器',
|
||||
name: 'MarkdownDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/dialog',
|
||||
title: '弹窗',
|
||||
name: 'DialogDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/detail',
|
||||
title: '详情',
|
||||
name: 'DetailDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/qrcode',
|
||||
title: '二维码',
|
||||
name: 'QrcodeDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/avatars',
|
||||
title: '头像组',
|
||||
name: 'AvatarsDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/highlight',
|
||||
title: '文字高亮',
|
||||
name: 'HighlightDemo'
|
||||
},
|
||||
{
|
||||
path: '/components-demo/watermark',
|
||||
name: 'WatermarkDemo',
|
||||
title: '水印'
|
||||
},
|
||||
{
|
||||
path: '/table-demo',
|
||||
title: '表格',
|
||||
name: 'TableDemo',
|
||||
children: [
|
||||
{
|
||||
path: '/table-demo/basic-table',
|
||||
title: '基础表格',
|
||||
name: 'BasicTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/page-table',
|
||||
title: '分页表格',
|
||||
name: 'PageTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/stripe-table',
|
||||
title: '带斑马纹表格',
|
||||
name: 'StripeTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/border-table',
|
||||
title: '带边框表格',
|
||||
name: 'BorderTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/state-table',
|
||||
title: '带状态表格',
|
||||
name: 'StateTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/fixed-header',
|
||||
title: '固定表头',
|
||||
name: 'FixedHeader'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/fixed-column',
|
||||
title: '固定列',
|
||||
name: 'FixedColumn'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/fixed-column-header',
|
||||
title: '固定列和表头',
|
||||
name: 'FixedColumnHeader'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/fluid-height',
|
||||
title: '流体高度',
|
||||
name: 'FluidHeight'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/multi-header',
|
||||
title: '多级表头',
|
||||
name: 'MultiHeader'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/single-choice',
|
||||
title: '单选',
|
||||
name: 'SingleChoice'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/multiple-choice',
|
||||
title: '多选',
|
||||
name: 'MultipleChoice'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/sort-table',
|
||||
title: '排序',
|
||||
name: 'SortTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/screen-table',
|
||||
title: '筛选',
|
||||
name: 'ScreenTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/expand-row',
|
||||
title: '展开行',
|
||||
name: 'ExpandRow'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/tree-and-load',
|
||||
title: '树形数据与懒加载',
|
||||
name: 'TreeAndLoad'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/custom-header',
|
||||
title: '自定义表头',
|
||||
name: 'CustomHeader'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/total-table',
|
||||
title: '表尾合计行',
|
||||
name: 'TotalTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/merge-table',
|
||||
title: '合并行或列',
|
||||
name: 'MergeTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/custom-index',
|
||||
title: '自定义索引',
|
||||
name: 'CustomIndex'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/table-demo/basic-table',
|
||||
title: '基础表格',
|
||||
name: 'BasicTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/page-table',
|
||||
title: '分页表格',
|
||||
name: 'PageTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/stripe-table',
|
||||
title: '带斑马纹表格',
|
||||
name: 'StripeTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/border-table',
|
||||
title: '带边框表格',
|
||||
name: 'BorderTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/state-table',
|
||||
title: '带状态表格',
|
||||
name: 'StateTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/fixed-header',
|
||||
title: '固定表头',
|
||||
name: 'FixedHeader'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/fixed-column',
|
||||
title: '固定列',
|
||||
name: 'FixedColumn'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/fixed-column-header',
|
||||
title: '固定列和表头',
|
||||
name: 'FixedColumnHeader'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/fluid-height',
|
||||
title: '流体高度',
|
||||
name: 'FluidHeight'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/multi-header',
|
||||
title: '多级表头',
|
||||
name: 'MultiHeader'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/single-choice',
|
||||
title: '单选',
|
||||
name: 'SingleChoice'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/multiple-choice',
|
||||
title: '多选',
|
||||
name: 'MultipleChoice'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/sort-table',
|
||||
title: '排序',
|
||||
name: 'SortTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/screen-table',
|
||||
title: '筛选',
|
||||
name: 'ScreenTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/expand-row',
|
||||
title: '展开行',
|
||||
name: 'ExpandRow'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/tree-and-load',
|
||||
title: '树形数据与懒加载',
|
||||
name: 'TreeAndLoad'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/custom-header',
|
||||
title: '自定义表头',
|
||||
name: 'CustomHeader'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/total-table',
|
||||
title: '表尾合计行',
|
||||
name: 'TotalTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/merge-table',
|
||||
title: '合并行或列',
|
||||
name: 'MergeTable'
|
||||
},
|
||||
{
|
||||
path: '/table-demo/custom-index',
|
||||
title: '自定义索引',
|
||||
name: 'CustomIndex'
|
||||
},
|
||||
{
|
||||
path: '/directives-demo',
|
||||
title: '自定义指令',
|
||||
name: 'DirectivesDemo',
|
||||
children: [
|
||||
{
|
||||
path: '/directives-demo/clipboard',
|
||||
title: 'Clipboard',
|
||||
name: 'ClipboardDemo'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/directives-demo/clipboard',
|
||||
title: 'Clipboard',
|
||||
name: 'ClipboardDemo'
|
||||
},
|
||||
{
|
||||
path: '/icon/index',
|
||||
title: '图标',
|
||||
name: 'Icons'
|
||||
},
|
||||
{
|
||||
path: '/level',
|
||||
title: '多级菜单缓存',
|
||||
name: 'Level',
|
||||
children: [
|
||||
{
|
||||
path: '/level/menu1',
|
||||
title: 'Menu1',
|
||||
name: 'Menu1Demo',
|
||||
children: [
|
||||
{
|
||||
path: '/level/menu1/menu1-1',
|
||||
title: 'Menu1-1',
|
||||
name: 'Menu11Demo',
|
||||
children: [
|
||||
{
|
||||
path: '/level/menu1/menu1-1/menu1-1-1',
|
||||
title: 'Menu1-1-1',
|
||||
name: 'Menu111Demo'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level/menu1/menu1-2',
|
||||
title: 'Menu1-2',
|
||||
name: 'Menu12Demo'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level/menu2',
|
||||
title: 'Menu2',
|
||||
name: 'Menu2Demo'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level/menu1',
|
||||
title: 'Menu1',
|
||||
name: 'Menu1Demo',
|
||||
children: [
|
||||
{
|
||||
path: '/level/menu1/menu1-1',
|
||||
title: 'Menu1-1',
|
||||
name: 'Menu11Demo',
|
||||
children: [
|
||||
{
|
||||
path: '/level/menu1/menu1-1/menu1-1-1',
|
||||
title: 'Menu1-1-1',
|
||||
name: 'Menu111Demo'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level/menu1/menu1-2',
|
||||
title: 'Menu1-2',
|
||||
name: 'Menu12Demo'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level/menu1/menu1-1',
|
||||
title: 'Menu1-1',
|
||||
name: 'Menu11Demo',
|
||||
children: [
|
||||
{
|
||||
path: '/level/menu1/menu1-1/menu1-1-1',
|
||||
title: 'Menu1-1-1',
|
||||
name: 'Menu111Demo'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level/menu1/menu1-1/menu1-1-1',
|
||||
title: 'Menu1-1-1',
|
||||
name: 'Menu111Demo'
|
||||
},
|
||||
{
|
||||
path: '/level/menu1/menu1-2',
|
||||
title: 'Menu1-2',
|
||||
name: 'Menu12Demo'
|
||||
},
|
||||
{
|
||||
path: '/level/menu2',
|
||||
title: 'Menu2',
|
||||
name: 'Menu2Demo'
|
||||
},
|
||||
{
|
||||
path: '/example-demo',
|
||||
title: '综合实例',
|
||||
name: 'ExampleDemo',
|
||||
children: [
|
||||
{
|
||||
path: '/example-demo/example-dialog',
|
||||
title: '列表综合实例-弹窗',
|
||||
name: 'ExampleDialog'
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-page',
|
||||
title: '列表综合实例-页面',
|
||||
name: 'ExamplePage'
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-add',
|
||||
title: '列表综合实例-新增',
|
||||
name: 'ExampleAdd'
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-edit',
|
||||
title: '列表综合实例-编辑',
|
||||
name: 'ExampleEdit'
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-detail',
|
||||
title: '列表综合实例-详情',
|
||||
name: 'ExampleDetail'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-dialog',
|
||||
title: '列表综合实例-弹窗',
|
||||
name: 'ExampleDialog'
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-page',
|
||||
title: '列表综合实例-页面',
|
||||
name: 'ExamplePage'
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-add',
|
||||
title: '列表综合实例-新增',
|
||||
name: 'ExampleAdd'
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-edit',
|
||||
title: '列表综合实例-编辑',
|
||||
name: 'ExampleEdit'
|
||||
},
|
||||
{
|
||||
path: '/example-demo/example-detail',
|
||||
title: '列表综合实例-详情',
|
||||
name: 'ExampleDetail'
|
||||
},
|
||||
{
|
||||
path: '/role-demo',
|
||||
title: '权限管理',
|
||||
name: 'RoleDemo',
|
||||
children: [
|
||||
{
|
||||
path: '/role-demo/user',
|
||||
title: '用户管理',
|
||||
name: 'User'
|
||||
},
|
||||
{
|
||||
path: '/role-demo/role',
|
||||
title: '角色管理',
|
||||
name: 'Role'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/role-demo/user',
|
||||
title: '用户管理',
|
||||
name: 'User'
|
||||
},
|
||||
{
|
||||
path: '/role-demo/role',
|
||||
title: '角色管理',
|
||||
name: 'Role'
|
||||
}
|
||||
]
|
||||
|
||||
export const checkedkeys = [
|
||||
'/components-demo',
|
||||
'/components-demo/echarts',
|
||||
'/components-demo/preview',
|
||||
'/components-demo/button',
|
||||
'/components-demo/message',
|
||||
'/components-demo/count-to',
|
||||
'/components-demo/search',
|
||||
'/components-demo/editor',
|
||||
'/components-demo/markdown',
|
||||
'/components-demo/dialog',
|
||||
'/components-demo/more',
|
||||
'/components-demo/detail',
|
||||
'/components-demo/qrcode',
|
||||
'/components-demo/avatars',
|
||||
'/components-demo/watermark',
|
||||
'/table-demo',
|
||||
'/table-demo/basic-table',
|
||||
'/table-demo/page-table',
|
||||
'/table-demo/stripe-table',
|
||||
'/table-demo/border-table',
|
||||
'/table-demo/state-table',
|
||||
'/table-demo/fixed-header',
|
||||
'/table-demo/fixed-column',
|
||||
'/table-demo/fixed-column-header',
|
||||
'/table-demo/fluid-height',
|
||||
'/table-demo/multi-header',
|
||||
'/table-demo/single-choice',
|
||||
'/table-demo/multiple-choice',
|
||||
'/table-demo/sort-table',
|
||||
'/table-demo/screen-table',
|
||||
'/table-demo/expand-row',
|
||||
'/table-demo/tree-and-load',
|
||||
'/table-demo/custom-header',
|
||||
'/table-demo/total-table',
|
||||
'/table-demo/merge-table',
|
||||
'/table-demo/custom-index',
|
||||
'/directives-demo',
|
||||
'/directives-demo/clipboard',
|
||||
'/icon/index',
|
||||
'/level',
|
||||
'/level/menu1',
|
||||
'/level/menu1/menu1-1',
|
||||
'/level/menu1/menu1-1/menu1-1-1',
|
||||
'/level/menu1/menu1-2',
|
||||
'/level/menu2',
|
||||
'/example-demo',
|
||||
'/example-demo/example-dialog',
|
||||
'/example-demo/example-page',
|
||||
'/example-demo/example-add',
|
||||
'/example-demo/example-edit',
|
||||
'/example-demo/example-detail',
|
||||
'/role-demo',
|
||||
'/role-demo/user',
|
||||
'/role-demo/role'
|
||||
]
|
||||
@@ -1,100 +0,0 @@
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
const { wsCache } = useCache()
|
||||
|
||||
import { checkedNodes, checkedkeys } from './admin-role'
|
||||
|
||||
import { checkedRoleNodes } from './test-role'
|
||||
|
||||
let List: {
|
||||
roleName: string
|
||||
id: string
|
||||
checkedNodes: any[]
|
||||
checkedkeys: any[]
|
||||
}[] = wsCache.get('roleList') || [
|
||||
{
|
||||
roleName: 'admin',
|
||||
id: '1',
|
||||
checkedNodes: checkedNodes,
|
||||
checkedkeys: checkedkeys
|
||||
},
|
||||
{
|
||||
roleName: 'test',
|
||||
id: '2',
|
||||
checkedNodes: checkedRoleNodes,
|
||||
checkedkeys: []
|
||||
}
|
||||
]
|
||||
|
||||
export default [
|
||||
// 列表接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/role/list',
|
||||
type: 'get',
|
||||
response: (config: any) => {
|
||||
const { roleName, pageIndex, pageSize } = config.query
|
||||
|
||||
const mockList = List.filter((item) => {
|
||||
if (roleName && item.roleName.indexOf(roleName) < 0) return false
|
||||
return true
|
||||
})
|
||||
const pageList = mockList.filter(
|
||||
(_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1)
|
||||
)
|
||||
|
||||
return {
|
||||
code: '0000',
|
||||
data: {
|
||||
total: mockList.length,
|
||||
list: pageList
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 详情接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/role/detail',
|
||||
type: 'get',
|
||||
response: (config: any) => {
|
||||
const { id } = config.query
|
||||
for (const role of List) {
|
||||
if (role.id === id) {
|
||||
return {
|
||||
code: '0000',
|
||||
data: role
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 保存接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/role/save',
|
||||
type: 'post',
|
||||
response: (config: any) => {
|
||||
const data = config.body
|
||||
if (!data.id) {
|
||||
List = [data].concat(List)
|
||||
return {
|
||||
code: '0000',
|
||||
data: 'success'
|
||||
}
|
||||
} else {
|
||||
List.map((item) => {
|
||||
if (item.id === data.id) {
|
||||
for (const key in item) {
|
||||
item[key] = data[key]
|
||||
}
|
||||
}
|
||||
})
|
||||
// 存在缓存中,避免刷新没有掉
|
||||
wsCache.set('roleList', List)
|
||||
return {
|
||||
code: '0000',
|
||||
data: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,460 +0,0 @@
|
||||
export const checkedRoleNodes = [
|
||||
{
|
||||
path: '/components-demo',
|
||||
component: '#',
|
||||
redirect: '/components-demo/echarts',
|
||||
name: 'ComponentsDemo',
|
||||
meta: {
|
||||
title: '功能组件',
|
||||
icon: 'component',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'echarts',
|
||||
component: 'views/components-demo/echarts/index',
|
||||
name: 'EchartsDemo',
|
||||
meta: {
|
||||
title: '图表'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'preview',
|
||||
component: 'views/components-demo/preview/index',
|
||||
name: 'PreviewDemo',
|
||||
meta: {
|
||||
title: '图片预览'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'message',
|
||||
component: 'views/components-demo/message/index',
|
||||
name: 'MessageDemo',
|
||||
meta: {
|
||||
title: '消息提示'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
component: 'views/components-demo/search/index',
|
||||
name: 'SearchDemo',
|
||||
meta: {
|
||||
title: '查询'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'editor',
|
||||
component: 'views/components-demo/editor/index',
|
||||
name: 'EditorDemo',
|
||||
meta: {
|
||||
title: '富文本编辑器'
|
||||
}
|
||||
},
|
||||
// {
|
||||
// path: 'markdown',
|
||||
// component: 'views/components-demo/markdown/index',
|
||||
// name: 'MarkdownDemo',
|
||||
// meta: {
|
||||
// title: 'markdown编辑器'
|
||||
// }
|
||||
// },
|
||||
{
|
||||
path: 'dialog',
|
||||
component: 'views/components-demo/dialog/index',
|
||||
name: 'DialogDemo',
|
||||
meta: {
|
||||
title: '弹窗'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'detail',
|
||||
component: 'views/components-demo/detail/index',
|
||||
name: 'DetailDemo',
|
||||
meta: {
|
||||
title: '详情'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'qrcode',
|
||||
component: 'views/components-demo/qrcode/index',
|
||||
name: 'QrcodeDemo',
|
||||
meta: {
|
||||
title: '二维码'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'avatars',
|
||||
component: 'views/components-demo/avatars/index',
|
||||
name: 'AvatarsDemo',
|
||||
meta: {
|
||||
title: '头像组'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'highlight',
|
||||
component: 'views/components-demo/highlight/index',
|
||||
name: 'HighlightDemo',
|
||||
meta: {
|
||||
title: '文字高亮'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'watermark',
|
||||
component: 'views/components-demo/watermark/index',
|
||||
name: 'WatermarkDemo',
|
||||
meta: {
|
||||
title: '水印'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/table-demo',
|
||||
component: '#',
|
||||
redirect: '/table-demo/basic-usage',
|
||||
name: 'TableDemo',
|
||||
meta: {
|
||||
title: '表格',
|
||||
icon: 'table',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic-table',
|
||||
component: 'views/table-demo/basic-table/index',
|
||||
name: 'BasicTable',
|
||||
meta: {
|
||||
title: '基础表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'page-table',
|
||||
component: 'views/table-demo/page-table/index',
|
||||
name: 'PageTable',
|
||||
meta: {
|
||||
title: '分页表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'stripe-table',
|
||||
component: 'views/table-demo/stripe-table/index',
|
||||
name: 'StripeTable',
|
||||
meta: {
|
||||
title: '带斑马纹表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'border-table',
|
||||
component: 'views/table-demo/border-table/index',
|
||||
name: 'BorderTable',
|
||||
meta: {
|
||||
title: '带边框表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'state-table',
|
||||
component: 'views/table-demo/state-table/index',
|
||||
name: 'StateTable',
|
||||
meta: {
|
||||
title: '带状态表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-header',
|
||||
component: 'views/table-demo/fixed-header/index',
|
||||
name: 'FixedHeader',
|
||||
meta: {
|
||||
title: '固定表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-column',
|
||||
component: 'views/table-demo/fixed-column/index',
|
||||
name: 'FixedColumn',
|
||||
meta: {
|
||||
title: '固定列'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-column-header',
|
||||
component: 'views/table-demo/fixed-column-header/index',
|
||||
name: 'FixedColumnHeader',
|
||||
meta: {
|
||||
title: '固定列和表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fluid-height',
|
||||
component: 'views/table-demo/fluid-height/index',
|
||||
name: 'FluidHeight',
|
||||
meta: {
|
||||
title: '流体高度'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'multi-header',
|
||||
component: 'views/table-demo/multi-header/index',
|
||||
name: 'MultiHeader',
|
||||
meta: {
|
||||
title: '多级表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'single-choice',
|
||||
component: 'views/table-demo/single-choice/index',
|
||||
name: 'SingleChoice',
|
||||
meta: {
|
||||
title: '单选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'multiple-choice',
|
||||
component: 'views/table-demo/multiple-choice/index',
|
||||
name: 'MultipleChoice',
|
||||
meta: {
|
||||
title: '多选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'sort-table',
|
||||
component: 'views/table-demo/sort-table/index',
|
||||
name: 'SortTable',
|
||||
meta: {
|
||||
title: '排序'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'screen-table',
|
||||
component: 'views/table-demo/screen-table/index',
|
||||
name: 'ScreenTable',
|
||||
meta: {
|
||||
title: '筛选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'expand-row',
|
||||
component: 'views/table-demo/expand-row/index',
|
||||
name: 'ExpandRow',
|
||||
meta: {
|
||||
title: '展开行'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tree-and-load',
|
||||
component: 'views/table-demo/tree-and-load/index',
|
||||
name: 'TreeAndLoad',
|
||||
meta: {
|
||||
title: '树形数据与懒加载'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'custom-header',
|
||||
component: 'views/table-demo/custom-header/index',
|
||||
name: 'CustomHeader',
|
||||
meta: {
|
||||
title: '自定义表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'total-table',
|
||||
component: 'views/table-demo/total-table/index',
|
||||
name: 'TotalTable',
|
||||
meta: {
|
||||
title: '表尾合计行'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'merge-table',
|
||||
component: 'views/table-demo/merge-table/index',
|
||||
name: 'MergeTable',
|
||||
meta: {
|
||||
title: '合并行或列'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'custom-index',
|
||||
component: 'views/table-demo/custom-index/index',
|
||||
name: 'CustomIndex',
|
||||
meta: {
|
||||
title: '自定义索引'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/icon',
|
||||
component: '#',
|
||||
name: 'IconsDemo',
|
||||
meta: {
|
||||
title: '图标',
|
||||
icon: 'icon'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/icons/index',
|
||||
name: 'Icons',
|
||||
meta: {
|
||||
title: '图标',
|
||||
icon: 'icon'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level',
|
||||
component: '#',
|
||||
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||
name: 'Level',
|
||||
meta: {
|
||||
title: '多级菜单缓存',
|
||||
icon: 'nested'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'menu1',
|
||||
name: 'Menu1Demo',
|
||||
component: '##Menu1Demo',
|
||||
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||
meta: {
|
||||
title: 'Menu1'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-1',
|
||||
name: 'Menu11Demo',
|
||||
component: '##Menu11Demo',
|
||||
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||
meta: {
|
||||
title: 'Menu1-1',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-1-1',
|
||||
name: 'Menu111Demo',
|
||||
component: 'views/level/menu111',
|
||||
meta: {
|
||||
title: 'Menu1-1-1'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'menu1-2',
|
||||
name: 'Menu12Demo',
|
||||
component: 'views/level/menu12',
|
||||
meta: {
|
||||
title: 'Menu1-2'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'menu2',
|
||||
name: 'Menu2Demo',
|
||||
component: 'views/level/menu2',
|
||||
meta: {
|
||||
title: 'Menu2'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/example-demo',
|
||||
component: '#',
|
||||
name: 'ExampleDemo',
|
||||
redirect: '/example-demo/example-dialog',
|
||||
meta: {
|
||||
alwaysShow: true,
|
||||
icon: 'example',
|
||||
title: '综合实例'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'example-dialog',
|
||||
component: 'views/example-demo/example-dialog/index',
|
||||
name: 'ExampleDialog',
|
||||
meta: {
|
||||
title: '列表综合实例-弹窗'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-page',
|
||||
component: 'views/example-demo/example-page/index',
|
||||
name: 'ExamplePage',
|
||||
meta: {
|
||||
title: '列表综合实例-页面'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-add',
|
||||
component: 'views/example-demo/example-page/example-add',
|
||||
name: 'ExampleAdd',
|
||||
meta: {
|
||||
title: '列表综合实例-新增',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-edit',
|
||||
component: 'views/example-demo/example-page/example-edit',
|
||||
name: 'ExampleEdit',
|
||||
meta: {
|
||||
title: '列表综合实例-编辑',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-detail',
|
||||
component: 'views/example-demo/example-page/example-detail',
|
||||
name: 'ExampleDetail',
|
||||
meta: {
|
||||
title: '列表综合实例-详情',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/role-demo',
|
||||
component: '#',
|
||||
redirect: '/role-demo/user',
|
||||
name: 'RoleDemo',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
icon: 'user',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
component: 'views/role-demo/user/index',
|
||||
name: 'User',
|
||||
meta: {
|
||||
title: '用户管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
component: 'views/role-demo/role/index',
|
||||
name: 'Role',
|
||||
meta: {
|
||||
title: '角色管理'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,71 +0,0 @@
|
||||
const List: {
|
||||
userName: string
|
||||
password: string
|
||||
role: string
|
||||
roleId: string
|
||||
}[] = [
|
||||
{
|
||||
userName: 'admin',
|
||||
password: 'admin',
|
||||
role: 'admin',
|
||||
roleId: '1'
|
||||
},
|
||||
{
|
||||
userName: 'test',
|
||||
password: 'test',
|
||||
role: 'test',
|
||||
roleId: '2'
|
||||
}
|
||||
]
|
||||
|
||||
export default [
|
||||
// 列表接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/user/list',
|
||||
type: 'get',
|
||||
response: (config: any) => {
|
||||
const { userName, pageIndex, pageSize } = config.query
|
||||
|
||||
const mockList = List.filter((item) => {
|
||||
if (userName && item.userName.indexOf(userName) < 0) return false
|
||||
return true
|
||||
})
|
||||
const pageList = mockList.filter(
|
||||
(_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1)
|
||||
)
|
||||
|
||||
return {
|
||||
code: '0000',
|
||||
data: {
|
||||
total: mockList.length,
|
||||
list: pageList
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 登录接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/user/login',
|
||||
type: 'post',
|
||||
response: (config: any) => {
|
||||
const data = config.body
|
||||
let hasUser = false
|
||||
for (const user of List) {
|
||||
if (user.userName === data.userName && user.password === data.passWord) {
|
||||
hasUser = true
|
||||
return {
|
||||
code: '0000',
|
||||
data: user
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasUser) {
|
||||
return {
|
||||
code: '500',
|
||||
message: '账号或密码错误'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -97,347 +97,10 @@ export const constantRouterMap: AppRouteRecordRaw[] = [
|
||||
meta: { title: '文档', icon: 'documentation' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/guide',
|
||||
component: Layout,
|
||||
name: 'Guide',
|
||||
meta: {},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('_v/guide/index.vue'),
|
||||
name: 'GuideDemo',
|
||||
meta: {
|
||||
title: '引导页',
|
||||
icon: 'guide'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export const asyncRouterMap: AppRouteRecordRaw[] = [
|
||||
{
|
||||
path: '/components-demo',
|
||||
component: Layout,
|
||||
redirect: '/components-demo/echarts',
|
||||
name: 'ComponentsDemo',
|
||||
meta: {
|
||||
title: '功能组件',
|
||||
icon: 'component',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'echarts',
|
||||
component: () => import('_v/components-demo/echarts/index.vue'),
|
||||
name: 'EchartsDemo',
|
||||
meta: {
|
||||
title: '图表'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'preview',
|
||||
component: () => import('_v/components-demo/preview/index.vue'),
|
||||
name: 'PreviewDemo',
|
||||
meta: {
|
||||
title: '图片预览'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'message',
|
||||
component: () => import('_v/components-demo/message/index.vue'),
|
||||
name: 'MessageDemo',
|
||||
meta: {
|
||||
title: '消息提示'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'count-to',
|
||||
component: () => import('_v/components-demo/count-to/index.vue'),
|
||||
name: 'CountToDemo',
|
||||
meta: {
|
||||
title: '数字动画'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
component: () => import('_v/components-demo/search/index.vue'),
|
||||
name: 'SearchDemo',
|
||||
meta: {
|
||||
title: '查询'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'editor',
|
||||
component: () => import('_v/components-demo/editor/index.vue'),
|
||||
name: 'EditorDemo',
|
||||
meta: {
|
||||
title: '富文本编辑器'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'dialog',
|
||||
component: () => import('_v/components-demo/dialog/index.vue'),
|
||||
name: 'DialogDemo',
|
||||
meta: {
|
||||
title: '弹窗'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'detail',
|
||||
component: () => import('_v/components-demo/detail/index.vue'),
|
||||
name: 'DetailDemo',
|
||||
meta: {
|
||||
title: '详情'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'qrcode',
|
||||
component: () => import('_v/components-demo/qrcode/index.vue'),
|
||||
name: 'QrcodeDemo',
|
||||
meta: {
|
||||
title: '二维码'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'avatars',
|
||||
component: () => import('_v/components-demo/avatars/index.vue'),
|
||||
name: 'AvatarsDemo',
|
||||
meta: {
|
||||
title: '头像组'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'highlight',
|
||||
component: () => import('_v/components-demo/highlight/index.vue'),
|
||||
name: 'HighlightDemo',
|
||||
meta: {
|
||||
title: '文字高亮'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'watermark',
|
||||
component: () => import('_v/components-demo/watermark/index.vue'),
|
||||
name: 'WatermarkDemo',
|
||||
meta: {
|
||||
title: '水印'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/table-demo',
|
||||
component: Layout,
|
||||
redirect: '/table-demo/basic-table',
|
||||
name: 'TableDemo',
|
||||
meta: {
|
||||
title: '表格',
|
||||
icon: 'table',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic-table',
|
||||
component: () => import('_v/table-demo/basic-table/index.vue'),
|
||||
name: 'BasicTable',
|
||||
meta: {
|
||||
title: '基础表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'page-table',
|
||||
component: () => import('_v/table-demo/page-table/index.vue'),
|
||||
name: 'PageTable',
|
||||
meta: {
|
||||
title: '分页表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'stripe-table',
|
||||
component: () => import('_v/table-demo/stripe-table/index.vue'),
|
||||
name: 'StripeTable',
|
||||
meta: {
|
||||
title: '带斑马纹表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'border-table',
|
||||
component: () => import('_v/table-demo/border-table/index.vue'),
|
||||
name: 'BorderTable',
|
||||
meta: {
|
||||
title: '带边框表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'state-table',
|
||||
component: () => import('_v/table-demo/state-table/index.vue'),
|
||||
name: 'StateTable',
|
||||
meta: {
|
||||
title: '带状态表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-header',
|
||||
component: () => import('_v/table-demo/fixed-header/index.vue'),
|
||||
name: 'FixedHeader',
|
||||
meta: {
|
||||
title: '固定表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-column',
|
||||
component: () => import('_v/table-demo/fixed-column/index.vue'),
|
||||
name: 'FixedColumn',
|
||||
meta: {
|
||||
title: '固定列'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-column-header',
|
||||
component: () => import('_v/table-demo/fixed-column-header/index.vue'),
|
||||
name: 'FixedColumnHeader',
|
||||
meta: {
|
||||
title: '固定列和表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fluid-height',
|
||||
component: () => import('_v/table-demo/fluid-height/index.vue'),
|
||||
name: 'FluidHeight',
|
||||
meta: {
|
||||
title: '流体高度'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'multi-header',
|
||||
component: () => import('_v/table-demo/multi-header/index.vue'),
|
||||
name: 'MultiHeader',
|
||||
meta: {
|
||||
title: '多级表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'single-choice',
|
||||
component: () => import('_v/table-demo/single-choice/index.vue'),
|
||||
name: 'SingleChoice',
|
||||
meta: {
|
||||
title: '单选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'multiple-choice',
|
||||
component: () => import('_v/table-demo/multiple-choice/index.vue'),
|
||||
name: 'MultipleChoice',
|
||||
meta: {
|
||||
title: '多选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'sort-table',
|
||||
component: () => import('_v/table-demo/sort-table/index.vue'),
|
||||
name: 'SortTable',
|
||||
meta: {
|
||||
title: '排序'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'screen-table',
|
||||
component: () => import('_v/table-demo/screen-table/index.vue'),
|
||||
name: 'ScreenTable',
|
||||
meta: {
|
||||
title: '筛选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'expand-row',
|
||||
component: () => import('_v/table-demo/expand-row/index.vue'),
|
||||
name: 'ExpandRow',
|
||||
meta: {
|
||||
title: '展开行'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tree-and-load',
|
||||
component: () => import('_v/table-demo/tree-and-load/index.vue'),
|
||||
name: 'TreeAndLoad',
|
||||
meta: {
|
||||
title: '树形数据与懒加载'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'custom-header',
|
||||
component: () => import('_v/table-demo/custom-header/index.vue'),
|
||||
name: 'CustomHeader',
|
||||
meta: {
|
||||
title: '自定义表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'total-table',
|
||||
component: () => import('_v/table-demo/total-table/index.vue'),
|
||||
name: 'TotalTable',
|
||||
meta: {
|
||||
title: '表尾合计行'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'merge-table',
|
||||
component: () => import('_v/table-demo/merge-table/index.vue'),
|
||||
name: 'MergeTable',
|
||||
meta: {
|
||||
title: '合并行或列'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'custom-index',
|
||||
component: () => import('_v/table-demo/custom-index/index.vue'),
|
||||
name: 'CustomIndex',
|
||||
meta: {
|
||||
title: '自定义索引'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/directives-demo',
|
||||
component: Layout,
|
||||
redirect: '/directives-demo/clipboard',
|
||||
name: 'DirectivesDemo',
|
||||
meta: {
|
||||
title: '自定义指令',
|
||||
icon: 'clipboard',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'clipboard',
|
||||
component: () => import('_v/directives-demo/clipboard/index.vue'),
|
||||
name: 'ClipboardDemo',
|
||||
meta: {
|
||||
title: 'Clipboard'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/icon',
|
||||
component: Layout,
|
||||
name: 'IconsDemo',
|
||||
meta: {},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('_v/icons/index.vue'),
|
||||
name: 'Icons',
|
||||
meta: {
|
||||
title: '图标',
|
||||
icon: 'icon'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/level',
|
||||
component: Layout,
|
||||
@@ -496,103 +159,6 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/example-demo',
|
||||
component: Layout,
|
||||
name: 'ExampleDemo',
|
||||
redirect: '/example-demo/example-dialog',
|
||||
meta: {
|
||||
alwaysShow: true,
|
||||
icon: 'example',
|
||||
title: '综合实例'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'example-dialog',
|
||||
component: () => import('_v/example-demo/example-dialog/index.vue'),
|
||||
name: 'ExampleDialog',
|
||||
meta: {
|
||||
title: '列表综合实例-弹窗'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-page',
|
||||
component: () => import('_v/example-demo/example-page/index.vue'),
|
||||
name: 'ExamplePage',
|
||||
meta: {
|
||||
title: '列表综合实例-页面'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-add',
|
||||
component: () => import('_v/example-demo/example-page/example-add.vue'),
|
||||
name: 'ExampleAdd',
|
||||
meta: {
|
||||
title: '列表综合实例-新增',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-edit',
|
||||
component: () => import('_v/example-demo/example-page/example-edit.vue'),
|
||||
name: 'ExampleEdit',
|
||||
meta: {
|
||||
title: '列表综合实例-编辑',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-detail',
|
||||
component: () => import('_v/example-demo/example-page/example-detail.vue'),
|
||||
name: 'ExampleDetail',
|
||||
meta: {
|
||||
title: '列表综合实例-详情',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/role-demo',
|
||||
component: Layout,
|
||||
redirect: '/role-demo/user',
|
||||
name: 'RoleDemo',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
icon: 'user',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
component: () => import('_v/role-demo/user/index.vue'),
|
||||
name: 'User',
|
||||
meta: {
|
||||
title: '用户管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
component: () => import('_v/role-demo/role/index.vue'),
|
||||
name: 'Role',
|
||||
meta: {
|
||||
title: '角色管理'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -2,20 +2,20 @@ import { defineStore } from 'pinia'
|
||||
import { asyncRouterMap, constantRouterMap } from '@/router'
|
||||
import { deepClone } from '@/utils'
|
||||
import { AppRouteRecordRaw } from '@/router/types'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
const { wsCache } = useCache()
|
||||
import { isExternal } from '@/utils/validate'
|
||||
// import { useCache } from '@/hooks/web/useCache'
|
||||
// const { wsCache } = useCache()
|
||||
// import { isExternal } from '@/utils/validate'
|
||||
import path from 'path-browserify'
|
||||
import { getParentLayout } from '@/router/utils'
|
||||
// import { getParentLayout } from '@/router/utils'
|
||||
import { store } from '../index'
|
||||
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
const appStore = useAppStoreWithOut()
|
||||
// import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
// const appStore = useAppStoreWithOut()
|
||||
|
||||
const modules = import.meta.glob('../../views/**/*.vue')
|
||||
// const modules = import.meta.glob('../../views/**/*.vue')
|
||||
|
||||
/* Layout */
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
// const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
export interface PermissionState {
|
||||
routers: AppRouteRecordRaw[]
|
||||
@@ -55,15 +55,17 @@ export const usePermissionStore = defineStore({
|
||||
generateRoutes(): Promise<unknown> {
|
||||
return new Promise<void>((resolve) => {
|
||||
// 路由权限控制
|
||||
let routerMap: AppRouteRecordRaw[] = []
|
||||
if (wsCache.get(appStore.getUserInfo).roleName === 'admin') {
|
||||
// 模拟前端控制权限
|
||||
routerMap = generateRoutesFn(deepClone(asyncRouterMap, ['component']))
|
||||
} else {
|
||||
// 模拟后端控制权限
|
||||
routerMap = getFilterRoutes(wsCache.get(appStore.getUserInfo).checkedNodes)
|
||||
}
|
||||
// const routerMap: AppRouteRecordRaw[] = generateRoutesFn(deepClone(asyncRouterMap, ['component']))
|
||||
// let routerMap: AppRouteRecordRaw[] = []
|
||||
// if (wsCache.get(appStore.getUserInfo).roleName === 'admin') {
|
||||
// // 模拟前端控制权限
|
||||
// routerMap = generateRoutesFn(deepClone(asyncRouterMap, ['component']))
|
||||
// } else {
|
||||
// // 模拟后端控制权限
|
||||
// routerMap = getFilterRoutes(wsCache.get(appStore.getUserInfo).checkedNodes)
|
||||
// }
|
||||
const routerMap: AppRouteRecordRaw[] = generateRoutesFn(
|
||||
deepClone(asyncRouterMap, ['component'])
|
||||
)
|
||||
// 动态路由,404一定要放到最后面
|
||||
this.addRouters = routerMap.concat([
|
||||
{
|
||||
@@ -100,39 +102,38 @@ function generateRoutesFn(routes: AppRouteRecordRaw[], basePath = '/'): AppRoute
|
||||
for (const route of routes) {
|
||||
// skip some route
|
||||
if (route.meta && route.meta.hidden && !route.meta.showMainRoute) {
|
||||
console.log(route)
|
||||
continue
|
||||
}
|
||||
|
||||
let onlyOneChild: Nullable<string> = null
|
||||
// let onlyOneChild: Nullable<string> = null
|
||||
|
||||
if (route.children && route.children.length === 1 && !route.meta.alwaysShow) {
|
||||
onlyOneChild = (
|
||||
isExternal(route.children[0].path)
|
||||
? route.children[0].path
|
||||
: path.resolve(path.resolve(basePath, route.path), route.children[0].path)
|
||||
) as string
|
||||
}
|
||||
// if (route.children && route.children.length === 1 && !route.meta.alwaysShow) {
|
||||
// onlyOneChild = (
|
||||
// isExternal(route.children[0].path)
|
||||
// ? route.children[0].path
|
||||
// : path.resolve(path.resolve(basePath, route.path), route.children[0].path)
|
||||
// ) as string
|
||||
// }
|
||||
|
||||
let data: Nullable<IObj> = null
|
||||
|
||||
// 如不需要路由权限,可注释以下逻辑
|
||||
// 权限过滤,通过获取登录信息里面的角色权限,动态的渲染菜单。
|
||||
const list = wsCache.get(appStore.getUserInfo).checkedNodes
|
||||
// 开发者可以根据实际情况进行扩展
|
||||
for (const item of list) {
|
||||
// 通过路径去匹配
|
||||
if (isExternal(item.path) && (onlyOneChild === item.path || route.path === item.path)) {
|
||||
data = Object.assign({}, route)
|
||||
} else {
|
||||
const routePath = path.resolve(basePath, onlyOneChild || route.path)
|
||||
if (routePath === item.path || (route.meta && route.meta.followRoute === item.path)) {
|
||||
data = Object.assign({}, route)
|
||||
}
|
||||
}
|
||||
}
|
||||
// const list = wsCache.get(appStore.getUserInfo).checkedNodes
|
||||
// // 开发者可以根据实际情况进行扩展
|
||||
// for (const item of list) {
|
||||
// // 通过路径去匹配
|
||||
// if (isExternal(item.path) && (onlyOneChild === item.path || route.path === item.path)) {
|
||||
// data = Object.assign({}, route)
|
||||
// } else {
|
||||
// const routePath = path.resolve(basePath, onlyOneChild || route.path)
|
||||
// if (routePath === item.path || (route.meta && route.meta.followRoute === item.path)) {
|
||||
// data = Object.assign({}, route)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 如不需要路由权限,解注释下面一行
|
||||
// data = Object.assign({}, route)
|
||||
data = Object.assign({}, route)
|
||||
|
||||
// recursive child routes
|
||||
if (route.children && data) {
|
||||
@@ -146,34 +147,34 @@ function generateRoutesFn(routes: AppRouteRecordRaw[], basePath = '/'): AppRoute
|
||||
}
|
||||
|
||||
// 模拟后端过滤路由
|
||||
function getFilterRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
|
||||
const res: AppRouteRecordRaw[] = []
|
||||
// function getFilterRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
|
||||
// const res: AppRouteRecordRaw[] = []
|
||||
|
||||
for (const route of routes) {
|
||||
const data: AppRouteRecordRaw | IObj = {
|
||||
path: route.path,
|
||||
name: route.name,
|
||||
redirect: route.redirect
|
||||
}
|
||||
data.meta = Object.assign({}, route.meta || {}, { title: route.meta.title })
|
||||
if (route.component) {
|
||||
// 动态加载路由文件,可根据实际情况进行自定义逻辑
|
||||
data.component = (
|
||||
(route.component as any) === '#'
|
||||
? Layout
|
||||
: (route.component as any).includes('##')
|
||||
? getParentLayout((route.component as any).split('##')[1])
|
||||
: modules[`../../${route.component}.vue`]
|
||||
) as any
|
||||
}
|
||||
// recursive child routes
|
||||
if (route.children) {
|
||||
data.children = getFilterRoutes(route.children)
|
||||
}
|
||||
res.push(data as AppRouteRecordRaw)
|
||||
}
|
||||
return res
|
||||
}
|
||||
// for (const route of routes) {
|
||||
// const data: AppRouteRecordRaw | IObj = {
|
||||
// path: route.path,
|
||||
// name: route.name,
|
||||
// redirect: route.redirect
|
||||
// }
|
||||
// data.meta = Object.assign({}, route.meta || {}, { title: route.meta.title })
|
||||
// if (route.component) {
|
||||
// // 动态加载路由文件,可根据实际情况进行自定义逻辑
|
||||
// data.component = (
|
||||
// (route.component as any) === '#'
|
||||
// ? Layout
|
||||
// : (route.component as any).includes('##')
|
||||
// ? getParentLayout((route.component as any).split('##')[1])
|
||||
// : modules[`../../${route.component}.vue`]
|
||||
// ) as any
|
||||
// }
|
||||
// // recursive child routes
|
||||
// if (route.children) {
|
||||
// data.children = getFilterRoutes(route.children)
|
||||
// }
|
||||
// res.push(data as AppRouteRecordRaw)
|
||||
// }
|
||||
// return res
|
||||
// }
|
||||
|
||||
export function usePermissionStoreWithOut() {
|
||||
return usePermissionStore(store)
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="头像组组件 -- 基础用法"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<avatars :data="data" />
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="头像组组件 -- 不显示tooltip"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<avatars :data="data" :tooltip="false" />
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="头像组组件 -- 最多展示5"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<avatars :data="data" :max="5" />
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="头像组组件 -- 展示头像"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<avatars show-avatar :data="data1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="AvatarsDemo">
|
||||
import { ref } from 'vue'
|
||||
import { AvatarConfig } from '_c/Avatars/types'
|
||||
import Avatars from '_c/Avatars/index.vue'
|
||||
|
||||
const data = ref<AvatarConfig[]>([
|
||||
{ text: '陈某某' },
|
||||
{ text: '李某某', type: 'success' },
|
||||
{ text: '张某某', type: 'danger' },
|
||||
{ text: '王某某', type: 'warning' },
|
||||
{ text: '龙某某' },
|
||||
{ text: '孙某某' },
|
||||
{ text: '刘某某' },
|
||||
{ text: '赵某某' }
|
||||
])
|
||||
const data1 = ref<AvatarConfig[]>([
|
||||
{
|
||||
text: '陈某某',
|
||||
url: 'https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2863969516,2611770076&fm=26&gp=0.jpg'
|
||||
},
|
||||
{
|
||||
text: '李某某',
|
||||
url: 'https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=465970198,3877503753&fm=26&gp=0.jpg'
|
||||
},
|
||||
{
|
||||
text: '张某某',
|
||||
url: 'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1857202600,3614139084&fm=26&gp=0.jpg'
|
||||
},
|
||||
{
|
||||
text: '王某某',
|
||||
url: 'https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1444080181,4150129244&fm=26&gp=0.jpg'
|
||||
},
|
||||
{
|
||||
text: '龙某某',
|
||||
url: 'https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2469786567,2163872639&fm=26&gp=0.jpg'
|
||||
},
|
||||
{
|
||||
text: '孙某某',
|
||||
url: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=4119236579,869456058&fm=26&gp=0.jpg'
|
||||
},
|
||||
{ text: '刘某某', url: 'xxxxx' },
|
||||
{ text: '赵某某' }
|
||||
])
|
||||
</script>
|
||||
@@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 vue-count-to 进行改造,支持所有 vue-count-to 参数。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<div class="count-to">
|
||||
<count-to
|
||||
ref="countRef"
|
||||
:start-val="startVal"
|
||||
:end-val="endVal"
|
||||
:duration="duration"
|
||||
:decimals="decimals"
|
||||
:separator="separator"
|
||||
:prefix="prefix"
|
||||
:suffix="suffix"
|
||||
:autoplay="autoplay"
|
||||
class="count-to__item"
|
||||
/>
|
||||
</div>
|
||||
<div class="action">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>startVal:</span><el-input-number v-model="startVal" :min="0" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>endVal:</span><el-input-number v-model="endVal" :min="1" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item">
|
||||
<span>duration:</span><el-input-number v-model="duration" :min="1000" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item"> <span>separator:</span><el-input v-model="separator" /> </div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item"> <span>prefix:</span><el-input v-model="prefix" /> </div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="action__item"> <span>suffix:</span><el-input v-model="suffix" /> </div>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<div style="margin-top: 20px; text-align: center">
|
||||
<el-button type="primary" @click="start">start</el-button>
|
||||
<el-button style="margin-left: 10px" @click="pauseResume">pause/resume</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CountToDemo">
|
||||
import { ref, unref } from 'vue'
|
||||
import CountTo from '_c/CountTo/index.vue'
|
||||
const countRef = ref<HTMLElement | null>(null)
|
||||
const startVal = ref<number>(0)
|
||||
const endVal = ref<number>(1314512)
|
||||
const duration = ref<number>(3000)
|
||||
const decimals = ref<number>(0)
|
||||
const separator = ref<string>(',')
|
||||
const prefix = ref<string>('¥ ')
|
||||
const suffix = ref<string>(' rmb')
|
||||
const autoplay = ref<boolean>(false)
|
||||
|
||||
function start(): void {
|
||||
;(unref(countRef) as any).start()
|
||||
}
|
||||
|
||||
function pauseResume(): void {
|
||||
;(unref(countRef) as any).pauseResume()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.count-to {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
|
||||
&__item {
|
||||
font-size: 80px;
|
||||
font-weight: bold;
|
||||
color: #f6416c;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-top: 20px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
padding: 0 15px;
|
||||
margin-bottom: 10px;
|
||||
align-items: center;
|
||||
|
||||
& > span {
|
||||
display: inline-block;
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
:deep(.el-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,218 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="详情组件。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<com-detail :data="data" :schema="schema" title="基础示例" message="辅助文字" />
|
||||
|
||||
<com-detail
|
||||
title="不可折叠"
|
||||
:data="data"
|
||||
:schema="schema"
|
||||
:collapsed="false"
|
||||
message="辅助文字"
|
||||
style="margin-top: 20px"
|
||||
/>
|
||||
|
||||
<com-detail title="没有辅助文字" :data="data" :schema="schema" style="margin-top: 20px" />
|
||||
|
||||
<com-detail
|
||||
title="没有边框"
|
||||
:data="data"
|
||||
:schema="schema"
|
||||
:border="false"
|
||||
style="margin-top: 20px"
|
||||
/>
|
||||
|
||||
<com-detail
|
||||
title="垂直布局"
|
||||
:data="data"
|
||||
:vertical="true"
|
||||
:schema="schema"
|
||||
style="margin-top: 20px"
|
||||
/>
|
||||
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:hide-required-asterisk="true"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
style="margin-top: 20px"
|
||||
>
|
||||
<com-detail title="与表单结合并自定义插槽" :data="form" :schema="fromSchema">
|
||||
<template #title="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #titleContent>
|
||||
<el-form-item prop="title">
|
||||
<el-input v-model="form.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template #author="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #authorContent>
|
||||
<el-form-item prop="author">
|
||||
<el-input v-model="form.author" placeholder="请输入作者" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template #display_time="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #display_timeContent>
|
||||
<el-form-item prop="display_time">
|
||||
<el-date-picker
|
||||
v-model="form.display_time"
|
||||
type="datetime"
|
||||
placeholder="请选择创建时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template #importance="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #importanceContent>
|
||||
<el-form-item prop="importance">
|
||||
<el-select v-model="form.importance" placeholder="请选择重要性" style="width: 100%">
|
||||
<el-option label="重要" value="3" />
|
||||
<el-option label="良好" value="2" />
|
||||
<el-option label="一般" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template #pageviews="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #pageviewsContent>
|
||||
<el-form-item prop="pageviews">
|
||||
<el-input-number
|
||||
v-model="form.pageviews"
|
||||
:min="0"
|
||||
:max="99999999"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</com-detail>
|
||||
<div style="margin-top: 15px; text-align: center">
|
||||
<el-button type="primary" @click="saveData">保存(控制台查看数据)</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DetailDemo">
|
||||
import { reactive, ref, unref } from 'vue'
|
||||
import { SchemaConfig } from '_c/ComDetail/types'
|
||||
|
||||
const formRef = ref<Nullable<any>>(null)
|
||||
|
||||
const requiredRule: {
|
||||
required: boolean
|
||||
message: string
|
||||
} = {
|
||||
required: true,
|
||||
message: '该项为必填项'
|
||||
}
|
||||
|
||||
const data = reactive<IObj>({
|
||||
username: 'chenkl',
|
||||
nickName: '梦似花落。',
|
||||
age: 26,
|
||||
phone: '13655971xxxx',
|
||||
email: '502431556@qq.com',
|
||||
addr: '这是一个很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的地址',
|
||||
sex: '男',
|
||||
certy: '35058319940712xxxx'
|
||||
})
|
||||
|
||||
const schema = reactive<SchemaConfig[]>([
|
||||
{
|
||||
field: 'username',
|
||||
label: '用户名'
|
||||
},
|
||||
{
|
||||
field: 'nickName',
|
||||
label: '昵称'
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
label: '联系电话'
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: '邮箱'
|
||||
},
|
||||
{
|
||||
field: 'addr',
|
||||
label: '地址',
|
||||
span: 24
|
||||
}
|
||||
])
|
||||
|
||||
const fromSchema = reactive<SchemaConfig[]>([
|
||||
{
|
||||
field: 'title',
|
||||
label: '标题',
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: '作者'
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: '创建时间'
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: '重要性'
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: '阅读数'
|
||||
}
|
||||
])
|
||||
|
||||
const form = reactive<IObj>({
|
||||
id: '', // id
|
||||
author: '', // 作者
|
||||
title: '', // 标题
|
||||
importance: '', // 重要性
|
||||
display_time: '', // 创建时间
|
||||
pageviews: 0 // 阅读数
|
||||
})
|
||||
|
||||
const rules = reactive<IObj>({
|
||||
title: [requiredRule],
|
||||
author: [requiredRule],
|
||||
importance: [requiredRule],
|
||||
display_time: [requiredRule],
|
||||
pageviews: [requiredRule]
|
||||
})
|
||||
|
||||
function saveData() {
|
||||
try {
|
||||
;(unref(formRef) as any).validate((valid) => {
|
||||
if (valid) {
|
||||
console.log(form)
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="对 Element 的 Dialog 组件进行二次封装,支持所有原生参数。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-button type="primary" @click="visible = true">打开弹窗</el-button>
|
||||
|
||||
<com-dialog v-model="visible" title="提示">
|
||||
<div style="height: 1000px"> 我是弹窗内容 </div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="visible = false">确定</el-button>
|
||||
</template>
|
||||
</com-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DialogDemo">
|
||||
import { ref } from 'vue'
|
||||
const visible = ref<boolean>(false)
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,306 +0,0 @@
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { EChartsOption as EChartsWordOption } from 'echarts-wordcloud'
|
||||
|
||||
export const lineOptions: EChartsOption = {
|
||||
xAxis: {
|
||||
data: [
|
||||
'一月',
|
||||
'二月',
|
||||
'三月',
|
||||
'四月',
|
||||
'五月',
|
||||
'六月',
|
||||
'七月',
|
||||
'八月',
|
||||
'九月',
|
||||
'十月',
|
||||
'十一月',
|
||||
'十二月'
|
||||
],
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
top: 30,
|
||||
containLabel: true
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
padding: [5, 10]
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['预期', '实际']
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '预期',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123],
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'cubicInOut'
|
||||
},
|
||||
{
|
||||
name: '实际',
|
||||
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: '用户访问来源',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '用户访问来源',
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['50%', '60%'],
|
||||
data: [
|
||||
{ value: 335, name: '直接访问' },
|
||||
{ value: 310, name: '邮件营销' },
|
||||
{ value: 234, name: '联盟广告' },
|
||||
{ value: 135, name: '视频广告' },
|
||||
{ value: 1548, name: '搜索引擎' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const barOptions: EChartsOption = {
|
||||
title: {
|
||||
text: '每周用户活跃量',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '活跃量',
|
||||
data: [13253, 34235, 26321, 12340, 24643, 1322, 1324],
|
||||
type: 'bar'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const pieOptions2: EChartsOption = {
|
||||
title: {
|
||||
text: '用户访问来源',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '访问来源',
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['50%', '50%'],
|
||||
data: [
|
||||
{
|
||||
value: 335,
|
||||
name: '直接访问'
|
||||
},
|
||||
{
|
||||
value: 310,
|
||||
name: '邮件营销'
|
||||
},
|
||||
{
|
||||
value: 274,
|
||||
name: '联盟广告'
|
||||
},
|
||||
{
|
||||
value: 235,
|
||||
name: '视频广告'
|
||||
},
|
||||
{
|
||||
value: 400,
|
||||
name: '搜索引擎'
|
||||
}
|
||||
].sort(function (a, b) {
|
||||
return a.value - b.value
|
||||
}),
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const wordOptions: EChartsWordOption = {
|
||||
tooltip: {},
|
||||
series: [
|
||||
{
|
||||
type: 'wordCloud',
|
||||
gridSize: 2,
|
||||
sizeRange: [12, 50],
|
||||
rotationRange: [-90, 90],
|
||||
shape: 'pentagon',
|
||||
width: 600,
|
||||
height: 400,
|
||||
drawOutOfBound: true,
|
||||
textStyle: {
|
||||
color: function () {
|
||||
return (
|
||||
'rgb(' +
|
||||
[
|
||||
Math.round(Math.random() * 160),
|
||||
Math.round(Math.random() * 160),
|
||||
Math.round(Math.random() * 160)
|
||||
].join(',') +
|
||||
')'
|
||||
)
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
textStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: '#333'
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
name: 'Sam S Club',
|
||||
value: 10000,
|
||||
textStyle: {
|
||||
color: 'black'
|
||||
},
|
||||
emphasis: {
|
||||
textStyle: {
|
||||
color: 'red'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Macys',
|
||||
value: 6181
|
||||
},
|
||||
{
|
||||
name: 'Amy Schumer',
|
||||
value: 4386
|
||||
},
|
||||
{
|
||||
name: 'Jurassic World',
|
||||
value: 4055
|
||||
},
|
||||
{
|
||||
name: 'Charter Communications',
|
||||
value: 2467
|
||||
},
|
||||
{
|
||||
name: 'Chick Fil A',
|
||||
value: 2244
|
||||
},
|
||||
{
|
||||
name: 'Planet Fitness',
|
||||
value: 1898
|
||||
},
|
||||
{
|
||||
name: 'Pitch Perfect',
|
||||
value: 1484
|
||||
},
|
||||
{
|
||||
name: 'Express',
|
||||
value: 1112
|
||||
},
|
||||
{
|
||||
name: 'Home',
|
||||
value: 965
|
||||
},
|
||||
{
|
||||
name: 'Johnny Depp',
|
||||
value: 847
|
||||
},
|
||||
{
|
||||
name: 'Lena Dunham',
|
||||
value: 582
|
||||
},
|
||||
{
|
||||
name: 'Lewis Hamilton',
|
||||
value: 555
|
||||
},
|
||||
{
|
||||
name: 'KXAN',
|
||||
value: 550
|
||||
},
|
||||
{
|
||||
name: 'Mary Ellen Mark',
|
||||
value: 462
|
||||
},
|
||||
{
|
||||
name: 'Farrah Abraham',
|
||||
value: 366
|
||||
},
|
||||
{
|
||||
name: 'Rita Ora',
|
||||
value: 360
|
||||
},
|
||||
{
|
||||
name: 'Serena Williams',
|
||||
value: 282
|
||||
},
|
||||
{
|
||||
name: 'NCAA baseball tournament',
|
||||
value: 273
|
||||
},
|
||||
{
|
||||
name: 'Point Break',
|
||||
value: 265
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="统一封装 Echart 组件,自适应宽度,只需传入 options 与 height 属性即可展示对应的图表。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10">
|
||||
<div class="chart-wrap">
|
||||
<echart :height="'300px'" :options="pieOptions" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="chart-wrap">
|
||||
<echart :options="barOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="chart-wrap">
|
||||
<echart :options="lineOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<div class="chart-wrap">
|
||||
<echart :options="pieOptions2" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<div class="chart-wrap">
|
||||
<echart :options="wordOptions" :height="'300px'" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="EchartsDemo">
|
||||
import { lineOptions, pieOptions, barOptions, pieOptions2, wordOptions } from './echart-data'
|
||||
import Echart from '_c/Echart/index.vue'
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-wrap {
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 wangeditor 封装的 富文本 组件。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<editor ref="editorRef" :value="content" @change="handleChange" />
|
||||
|
||||
<div style="margin-top: 20px; text-align: center">
|
||||
<el-button @click="showHtml"> 获取TTML(请在控制台查看) </el-button>
|
||||
<el-button @click="showText"> 获取TEXT(请在控制台查看) </el-button>
|
||||
<el-button @click="showJson"> 获取JSON(请在控制台查看) </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="EditorDemo">
|
||||
import { ref, unref } from 'vue'
|
||||
import Editor from '_c/Editor/index.vue'
|
||||
|
||||
const content = ref<string>('默认展示数据')
|
||||
const editorRef = ref<Nullable<any>>(null)
|
||||
|
||||
function handleChange(html: string) {
|
||||
console.log(html)
|
||||
}
|
||||
|
||||
function showHtml() {
|
||||
console.log((unref(editorRef) as any).getHtml())
|
||||
}
|
||||
|
||||
function showText() {
|
||||
console.log((unref(editorRef) as any).getText())
|
||||
}
|
||||
|
||||
function showJson() {
|
||||
console.log((unref(editorRef) as any).getJSON())
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="文字高亮组件 -- 基础用法"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<highlight :keys="['vue-element-plus-admin', 'vue-element-admin', 'vben-admin']">
|
||||
vue-element-plus-admin是一个基于vue3、vite2、element-plus的后台解决方案,借鉴了vue-element-admin和vben-admin的写法和优点。内置了动态路由,权限验证,典型的业务模型,丰富的功能组件,并且提供了多页配置,开箱即用,可以用来作为项目的启动模版。它可以帮助你快速搭建企业级中后台产品原型,也可以作为一个示例,用于学习。
|
||||
</highlight>
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="文字高亮组件 -- 点击事件"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<highlight
|
||||
:keys="['vue-element-plus-admin', 'vue-element-admin', 'vben-admin']"
|
||||
@click="keyClick"
|
||||
>
|
||||
vue-element-plus-admin是一个基于vue3、vite2、element-plus的后台解决方案,借鉴了vue-element-admin和vben-admin的写法和优点。内置了动态路由,权限验证,典型的业务模型,丰富的功能组件,并且提供了多页配置,开箱即用,可以用来作为项目的启动模版。它可以帮助你快速搭建企业级中后台产品原型,也可以作为一个示例,用于学习。
|
||||
</highlight>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="HighlightDemo">
|
||||
import Highlight from '_c/Highlight/index'
|
||||
import { Message } from '_c/Message'
|
||||
function keyClick(key: string) {
|
||||
Message.success(key)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,24 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="二次封装 Element 的 Message 组件,每次只显示最新一条消息,避免出现太多消息提示导致不美观。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-button @click="show">显示</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="MessageDemo">
|
||||
import { ref } from 'vue'
|
||||
import { Message } from '_c/Message'
|
||||
const count = ref<number>(0)
|
||||
function show() {
|
||||
count.value = count.value + 1
|
||||
Message.success('这是成功消息' + count.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,113 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="抽取于 Element 的图片预览组件进行改造,实现函数式调用组件,无需基于图片进行点击预览。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="有底图预览。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<div class="img-wrap">
|
||||
<div
|
||||
v-for="(item, $index) in imgList"
|
||||
:key="item"
|
||||
class="img-item"
|
||||
@click="showHasImg($index)"
|
||||
>
|
||||
<img :src="item" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="无底图预览。"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<el-button type="primary" @click="showNoImg">点击预览</el-button>
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="点击事件,包含图片点击事件以及关闭事件。"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<el-button type="primary" @click="showImg">点击预览</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PreviewDemo">
|
||||
import { ref } from 'vue'
|
||||
import { createImgPreview } from '_c/Preview'
|
||||
import { Message } from '_c/Message'
|
||||
|
||||
const imgList = ref<string[]>([
|
||||
'https://img1.baidu.com/it/u=657828739,1486746195&fm=26&fmt=auto&gp=0.jpg',
|
||||
'https://img0.baidu.com/it/u=3114228356,677481409&fm=26&fmt=auto&gp=0.jpg',
|
||||
'https://img1.baidu.com/it/u=508846955,3814747122&fm=26&fmt=auto&gp=0.jpg',
|
||||
'https://img1.baidu.com/it/u=3536647690,3616605490&fm=26&fmt=auto&gp=0.jpg',
|
||||
'https://img1.baidu.com/it/u=4087287201,1148061266&fm=26&fmt=auto&gp=0.jpg',
|
||||
'https://img2.baidu.com/it/u=3429163260,2974496379&fm=26&fmt=auto&gp=0.jpg'
|
||||
])
|
||||
|
||||
function showHasImg(i: number) {
|
||||
createImgPreview({
|
||||
index: i,
|
||||
imageList: imgList.value
|
||||
})
|
||||
}
|
||||
|
||||
function showNoImg() {
|
||||
createImgPreview({
|
||||
index: 0,
|
||||
imageList: imgList.value
|
||||
})
|
||||
}
|
||||
|
||||
function showImg() {
|
||||
createImgPreview({
|
||||
index: 0,
|
||||
imageList: imgList.value,
|
||||
onClose: (i: number) => {
|
||||
Message.info('关闭的图片索引:' + i)
|
||||
},
|
||||
onSelect: (i: number) => {
|
||||
Message.info('当前点击的图片索引:' + i)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.img-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.img-item {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
margin: 0 10px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,90 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div class="title-item">基础用法,默认canvas</div>
|
||||
<qrcode text="vue-element-plus-admin" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-item">img标签</div>
|
||||
<qrcode text="vue-element-plus-admin" tag="img" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-item">样式配置</div>
|
||||
<qrcode
|
||||
text="vue-element-plus-admin"
|
||||
:options="{
|
||||
color: {
|
||||
dark: '#55D187',
|
||||
light: '#2d8cf0'
|
||||
}
|
||||
}"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-item">点击</div>
|
||||
<qrcode text="vue-element-plus-admin" @click="codeClick" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-item">异步内容</div>
|
||||
<qrcode :text="text" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-item">二维码失效</div>
|
||||
<qrcode text="vue-element-plus-admin" :disabled="true" @disabled-click="disabledClick" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-item">logo配置</div>
|
||||
<qrcode text="vue-element-plus-admin" :logo="logoImg" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-item">logo样式配置</div>
|
||||
<qrcode
|
||||
text="vue-element-plus-admin"
|
||||
:logo="{
|
||||
src: logoImg,
|
||||
logoSize: 0.2,
|
||||
borderSize: 0.05,
|
||||
borderRadius: 50,
|
||||
bgColor: 'blue'
|
||||
}"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-item">大小配置</div>
|
||||
<qrcode text="vue-element-plus-admin" :width="300" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="QrcodeDemo">
|
||||
import { ref } from 'vue'
|
||||
import Qrcode from '_c/Qrcode/index.vue'
|
||||
import { Message } from '_c/Message'
|
||||
import logoImg from '@/assets/img/logo.png'
|
||||
|
||||
const text = ref<string>('')
|
||||
setTimeout(() => {
|
||||
text.value = '我是异步生成的内容'
|
||||
}, 3000)
|
||||
|
||||
function codeClick() {
|
||||
Message.info('我被点击了。')
|
||||
}
|
||||
function disabledClick() {
|
||||
Message.info('我失效被点击了。')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-col {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
|
||||
.title-item {
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,149 +0,0 @@
|
||||
export const classicData = [
|
||||
{
|
||||
label: '即时配送',
|
||||
value: true,
|
||||
itemType: 'switch',
|
||||
field: 'delivery'
|
||||
},
|
||||
{
|
||||
label: '活动名称',
|
||||
value: '',
|
||||
itemType: 'input',
|
||||
field: 'name',
|
||||
placeholder: '活动名称',
|
||||
clearable: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入活动名称'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '活动区域',
|
||||
value: '',
|
||||
itemType: 'select',
|
||||
placeholder: '活动区域',
|
||||
clearable: true,
|
||||
field: 'region',
|
||||
options: [
|
||||
{
|
||||
title: '区域一',
|
||||
value: 'fujian'
|
||||
},
|
||||
{
|
||||
title: '区域二',
|
||||
value: 'beijing'
|
||||
}
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
itemType: 'string',
|
||||
required: true,
|
||||
message: '请选择活动区域'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '特殊资源',
|
||||
value: '2',
|
||||
itemType: 'radio',
|
||||
field: 'resource',
|
||||
radioType: 'button', // button or radio
|
||||
options: [
|
||||
{
|
||||
label: '线上品牌商赞助',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: '线下场地免费',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
},
|
||||
// {
|
||||
// label: '组织机构',
|
||||
// value: [],
|
||||
// itemType: 'treeSelect',
|
||||
// field: 'company',
|
||||
// allowClear: true,
|
||||
// placeholder: '请选择组织机构',
|
||||
// treeCheckable: false,
|
||||
// maxTagCount: 2,
|
||||
// options: [
|
||||
// {
|
||||
// title: 'Node1',
|
||||
// value: '0-0',
|
||||
// key: '0-0',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Child Node1',
|
||||
// value: '0-0-0',
|
||||
// key: '0-0-0'
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// title: 'Node2',
|
||||
// value: '0-1',
|
||||
// key: '0-1',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Child Node3',
|
||||
// value: '0-1-0',
|
||||
// key: '0-1-0',
|
||||
// disabled: true
|
||||
// },
|
||||
// {
|
||||
// title: 'Child Node4',
|
||||
// value: '0-1-1',
|
||||
// key: '0-1-1'
|
||||
// },
|
||||
// {
|
||||
// title: 'Child Node5',
|
||||
// value: '0-1-2',
|
||||
// key: '0-1-2'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
label: '日选择器',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date1',
|
||||
clearable: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择日期'
|
||||
},
|
||||
{
|
||||
label: '月选择器',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date2',
|
||||
clearable: true,
|
||||
format: 'YYYY-MM',
|
||||
placeholder: '请选择日期'
|
||||
},
|
||||
{
|
||||
label: '范围选择器',
|
||||
value: [],
|
||||
itemType: 'datePicker',
|
||||
field: 'date3',
|
||||
clearable: true,
|
||||
type: 'daterange',
|
||||
rangeSeparator: '至',
|
||||
startPlaceholder: '开始日期',
|
||||
endPlaceholder: '结束日期'
|
||||
},
|
||||
{
|
||||
label: '周选择器',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date4',
|
||||
type: 'week',
|
||||
clearable: true,
|
||||
placeholder: '请选择日期'
|
||||
}
|
||||
]
|
||||
@@ -1,95 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="封装 Element 的 Form 组件,实现查询、重置等功能,并提供了三种布局风格。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="经典风格。"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<div class="searh">
|
||||
<com-search :data="classicData" @search-submit="searchSubmit1" @reset-submit="resetSubmit1" />
|
||||
<div> 查询/重置后的数据:{{ formData1 }} </div>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="底部操作按钮风格。"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<div class="searh">
|
||||
<com-search
|
||||
layout="bottom"
|
||||
:data="classicData"
|
||||
@search-submit="searchSubmit2"
|
||||
@reset-submit="resetSubmit2"
|
||||
/>
|
||||
<div> 查询/重置后的数据:{{ formData2 }} </div>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="右侧操作按钮风格。"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<div class="searh">
|
||||
<com-search
|
||||
layout="right"
|
||||
:data="classicData"
|
||||
@search-submit="searchSubmit3"
|
||||
@reset-submit="resetSubmit3"
|
||||
/>
|
||||
<div> 查询/重置后的数据:{{ formData3 }} </div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="SearchDemo">
|
||||
import { ref } from 'vue'
|
||||
import { classicData } from './data'
|
||||
const formData1 = ref<Nullable<IObj>>(null)
|
||||
const formData2 = ref<Nullable<IObj>>(null)
|
||||
const formData3 = ref<Nullable<IObj>>(null)
|
||||
|
||||
function searchSubmit1(data: any): void {
|
||||
formData1.value = data
|
||||
}
|
||||
|
||||
function resetSubmit1(data: any): void {
|
||||
formData1.value = data
|
||||
}
|
||||
|
||||
function searchSubmit2(data: any): void {
|
||||
formData2.value = data
|
||||
}
|
||||
|
||||
function resetSubmit2(data: any): void {
|
||||
formData2.value = data
|
||||
}
|
||||
|
||||
function searchSubmit3(data: any): void {
|
||||
formData3.value = data
|
||||
}
|
||||
|
||||
function resetSubmit3(data: any): void {
|
||||
formData3.value = data
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.searh {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -1,26 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="useWatermark,为整个系统提供水印功能。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-button type="primary" @click="setWatermark('vue-element-plus-admin')">创建水印</el-button>
|
||||
<el-button type="danger" @click="clear">清除水印</el-button>
|
||||
<el-button type="warning" @click="setWatermark('vue-element-plus-admin-new')">
|
||||
重置水印
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Watermark">
|
||||
import { onBeforeUnmount } from 'vue'
|
||||
import { useWatermark } from '@/hooks/web/useWatermark'
|
||||
const { setWatermark, clear } = useWatermark()
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clear()
|
||||
})
|
||||
</script>
|
||||
@@ -1,147 +0,0 @@
|
||||
<template>
|
||||
<el-row :gutter="20" class="panel-group">
|
||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||||
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
|
||||
<div class="card-panel-icon-wrapper icon-people">
|
||||
<svg-icon icon-class="peoples" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">新增用户</div>
|
||||
<count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||||
<div class="card-panel" @click="handleSetLineChartData('messages')">
|
||||
<div class="card-panel-icon-wrapper icon-message">
|
||||
<svg-icon icon-class="message" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">未读信息</div>
|
||||
<count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||||
<div class="card-panel" @click="handleSetLineChartData('purchases')">
|
||||
<div class="card-panel-icon-wrapper icon-money">
|
||||
<svg-icon icon-class="money" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">成交金额</div>
|
||||
<count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||||
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
|
||||
<div class="card-panel-icon-wrapper icon-shopping">
|
||||
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">购物总量</div>
|
||||
<count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PanelGroup">
|
||||
import CountTo from '_c/CountTo/index.vue'
|
||||
|
||||
const emit = defineEmits(['handleSetLineChartData'])
|
||||
|
||||
function handleSetLineChartData(type: string) {
|
||||
emit('handleSetLineChartData', type)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.panel-group {
|
||||
.card-panel-col {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-panel {
|
||||
position: relative;
|
||||
height: 108px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
border-color: rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:hover {
|
||||
.card-panel-icon-wrapper {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon-people {
|
||||
background: #40c9c6;
|
||||
}
|
||||
|
||||
.icon-message {
|
||||
background: #36a3f7;
|
||||
}
|
||||
|
||||
.icon-money {
|
||||
background: #f4516c;
|
||||
}
|
||||
|
||||
.icon-shopping {
|
||||
background: #34bfa3;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-people {
|
||||
color: #40c9c6;
|
||||
}
|
||||
|
||||
.icon-message {
|
||||
color: #36a3f7;
|
||||
}
|
||||
|
||||
.icon-money {
|
||||
color: #f4516c;
|
||||
}
|
||||
|
||||
.icon-shopping {
|
||||
color: #34bfa3;
|
||||
}
|
||||
|
||||
.card-panel-icon-wrapper {
|
||||
float: left;
|
||||
padding: 16px;
|
||||
margin: 14px 0 0 14px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.38s ease-out;
|
||||
}
|
||||
|
||||
.card-panel-icon {
|
||||
float: left;
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.card-panel-description {
|
||||
float: right;
|
||||
margin: 26px;
|
||||
margin-left: 0;
|
||||
font-weight: bold;
|
||||
|
||||
.card-panel-text {
|
||||
margin-bottom: 12px;
|
||||
font-size: 16px;
|
||||
line-height: 18px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.card-panel-num {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<panel-group />
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10">
|
||||
<div class="chart__wrap">
|
||||
@@ -22,7 +21,6 @@
|
||||
<script setup lang="ts" name="Dashboard">
|
||||
import { lineOptions, pieOptions, barOptions } from './echart-data'
|
||||
import Echart from '_c/Echart/index.vue'
|
||||
import PanelGroup from './components/PanelGroup.vue'
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="自定义指令:v-clipboard,用于复制文本。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基础示例。"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<div class="input__wrap">
|
||||
<el-input v-model="inputVal1" placeholder="请输入要复制的文本" />
|
||||
<el-button v-clipboard="inputVal1" type="primary">复制</el-button>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="自定义回调方法。"
|
||||
type="info"
|
||||
style="margin-top: 20px; margin-bottom: 20px"
|
||||
/>
|
||||
<div class="input__wrap">
|
||||
<el-input v-model="inputVal2" placeholder="请输入要复制的文本" />
|
||||
<el-button
|
||||
v-clipboard="inputVal2"
|
||||
v-clipboard:success="clipboardSuccess"
|
||||
v-clipboard:error="clipboardError"
|
||||
type="primary"
|
||||
>复制</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DirectivesDemo">
|
||||
import { ref } from 'vue'
|
||||
import { Message } from '_c/Message'
|
||||
const inputVal1 = ref<string>('')
|
||||
const inputVal2 = ref<string>('')
|
||||
function clipboardSuccess(val: any) {
|
||||
Message.success('我是自定义成功回调:' + val.text)
|
||||
}
|
||||
function clipboardError() {
|
||||
Message.error('我是自定义失败回调')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.input__wrap {
|
||||
display: flex;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -1,17 +0,0 @@
|
||||
import fetch from '@/axios-config'
|
||||
|
||||
export const getExampleListApi = ({ params }: any) => {
|
||||
return fetch({ url: '/example/list', method: 'get', params })
|
||||
}
|
||||
|
||||
export const delsExampApi = ({ data }: any) => {
|
||||
return fetch({ url: '/example/delete', method: 'post', data })
|
||||
}
|
||||
|
||||
export const setExampApi = ({ data }: any) => {
|
||||
return fetch({ url: '/example/save', method: 'post', data })
|
||||
}
|
||||
|
||||
export const getExampDetApi = ({ params }: any) => {
|
||||
return fetch({ url: '/example/detail', method: 'get', params })
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<com-detail :data="form" :schema="fromSchema" :collapsed="false" title="文章详情">
|
||||
<template #contentContent="scope">
|
||||
<div v-html="scope.row.content"></div>
|
||||
</template>
|
||||
</com-detail>
|
||||
<div class="dialong__button--wrap">
|
||||
<el-button @click="close">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Detail">
|
||||
import { PropType, reactive } from 'vue'
|
||||
import { getExampDetApi } from '../api'
|
||||
import { SchemaConfig } from '_c/ComDetail/types'
|
||||
|
||||
const fromSchema: SchemaConfig[] = [
|
||||
{
|
||||
field: 'title',
|
||||
label: '标题',
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: '作者'
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: '创建时间'
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: '重要性'
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: '阅读数'
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: '内容',
|
||||
span: 24
|
||||
}
|
||||
]
|
||||
|
||||
const props = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<Nullable<IObj>>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const form = reactive<IObj>({
|
||||
id: '', // id
|
||||
author: '', // 作者
|
||||
title: '', // 标题
|
||||
content: '', // 内容
|
||||
importance: '', // 重要性
|
||||
display_time: '', // 创建时间
|
||||
pageviews: 0 // 阅读数
|
||||
})
|
||||
|
||||
async function getDet() {
|
||||
if (props.info) {
|
||||
const id = props.info.id
|
||||
try {
|
||||
const res: any = await getExampDetApi({
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
if (res) {
|
||||
for (const key in form) {
|
||||
if (key === 'importance') {
|
||||
form[key] = (res.data[key] as number).toString()
|
||||
} else {
|
||||
form[key] = res.data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
getDet()
|
||||
</script>
|
||||
@@ -1,158 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="title" label="标题">
|
||||
<el-input v-model="form.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="author" label="作者">
|
||||
<el-input v-model="form.author" placeholder="请输入作者" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="display_time" label="创建时间">
|
||||
<el-date-picker
|
||||
v-model="form.display_time"
|
||||
type="datetime"
|
||||
placeholder="请选择创建时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="importance" label="重要性">
|
||||
<el-select v-model="form.importance" placeholder="请选择重要性" style="width: 100%">
|
||||
<el-option label="重要" value="3" />
|
||||
<el-option label="良好" value="2" />
|
||||
<el-option label="一般" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="pageviews" label="阅读数">
|
||||
<el-input-number
|
||||
v-model="form.pageviews"
|
||||
:min="0"
|
||||
:max="99999999"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="content" label="内容">
|
||||
<editor ref="editorRef" :value="form.content" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="dialong__button--wrap">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button :loading="subLoading" type="primary" @click="setListData">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="IfnoWrite">
|
||||
import { PropType, reactive, ref } from 'vue'
|
||||
import { setExampApi, getExampDetApi } from '../api'
|
||||
import Editor from '_c/Editor/index.vue'
|
||||
import { Message } from '_c/Message'
|
||||
|
||||
const requiredRule: {
|
||||
required: boolean
|
||||
message: string
|
||||
} = {
|
||||
required: true,
|
||||
message: '该项为必填项'
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<Nullable<IObj>>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['success', 'close'])
|
||||
|
||||
const editorRef = ref<Nullable<HTMLElement>>(null)
|
||||
const formRef = ref<Nullable<HTMLElement>>(null)
|
||||
|
||||
const subLoading = ref<boolean>(false)
|
||||
const form = reactive<IObj>({
|
||||
id: '', // id
|
||||
author: '', // 作者
|
||||
title: '', // 标题
|
||||
content: '', // 内容
|
||||
importance: '', // 重要性
|
||||
display_time: '', // 创建时间
|
||||
pageviews: 0 // 阅读数
|
||||
})
|
||||
const rules = reactive<IObj>({
|
||||
title: [requiredRule],
|
||||
author: [requiredRule],
|
||||
content: [requiredRule],
|
||||
importance: [requiredRule],
|
||||
display_time: [requiredRule],
|
||||
pageviews: [requiredRule]
|
||||
})
|
||||
|
||||
async function getDet() {
|
||||
if (props.info) {
|
||||
const id = props.info.id
|
||||
try {
|
||||
const res: any = await getExampDetApi({
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
if (res) {
|
||||
for (const key in form) {
|
||||
if (key === 'importance') {
|
||||
form[key] = (res.data[key] as number).toString()
|
||||
} else {
|
||||
form[key] = res.data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增或者编辑
|
||||
function setListData() {
|
||||
try {
|
||||
subLoading.value = true
|
||||
form.content = (editorRef.value as any).getHtml()
|
||||
;(formRef.value as any).validate(async (valid) => {
|
||||
if (valid) {
|
||||
const res = await setExampApi({
|
||||
data: form
|
||||
})
|
||||
if (res) {
|
||||
Message.success(form.id ? '编辑成功' : '新增成功')
|
||||
emit('success', form.id ? 'edit' : 'add')
|
||||
}
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
subLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
getDet()
|
||||
</script>
|
||||
@@ -1,18 +0,0 @@
|
||||
export interface InfoWriteParams {
|
||||
title: string
|
||||
id?: string
|
||||
author: string
|
||||
content: string
|
||||
importance: string
|
||||
display_time: string
|
||||
pageviews: number
|
||||
}
|
||||
|
||||
export interface InfoWriteRules {
|
||||
title?: any[]
|
||||
author?: any[]
|
||||
content?: any[]
|
||||
importance?: any[]
|
||||
display_time?: any[]
|
||||
pageviews?: any[]
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="search__example--wrap">
|
||||
<com-search :data="searchData" @search-submit="searchSubmit" @reset-submit="resetSubmit" />
|
||||
</div>
|
||||
|
||||
<div class="button__example--wrap">
|
||||
<el-button type="primary" icon="el-icon-circle-plus-outline" @click="open(null, 'InfoWrite')">
|
||||
新增
|
||||
</el-button>
|
||||
<el-button type="danger" icon="el-icon-delete" @click="dels">删除</el-button>
|
||||
</div>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
selection
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
currentPage: defaultParams.pageIndex,
|
||||
total: total,
|
||||
onSizeChange: handleSizeChange,
|
||||
onCurrentChange: handleCurrentChange
|
||||
}"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<template #importance="scope">
|
||||
<el-tag
|
||||
:type="
|
||||
scope.row.importance === 3
|
||||
? 'success'
|
||||
: scope.row.importance === 2
|
||||
? 'warning'
|
||||
: 'danger'
|
||||
"
|
||||
>
|
||||
{{ scope.row.importance === 3 ? '重要' : scope.row.importance === 2 ? '良好' : '一般' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #action="scope">
|
||||
<el-button type="primary" size="mini" @click="open(scope.row, 'InfoWrite')">编辑</el-button>
|
||||
<el-button type="success" size="mini" @click="open(scope.row, 'Detail')">查看</el-button>
|
||||
<el-button type="danger" size="mini" @click="dels(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
|
||||
<com-dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<info-write
|
||||
v-if="comName === 'InfoWrite' && dialogVisible"
|
||||
:info="rowData"
|
||||
@close="toggleVisible"
|
||||
@success="refreshTable"
|
||||
/>
|
||||
<detail v-if="comName === 'Detail' && dialogVisible" :info="rowData" @close="toggleVisible" />
|
||||
</com-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ExampleDialog">
|
||||
import { getExampleListApi, delsExampApi } from './api'
|
||||
import { useWork } from '@/hooks/work/useWork'
|
||||
import InfoWrite from './components/InfoWrite.vue'
|
||||
import Detail from './components/Detail.vue'
|
||||
const {
|
||||
defaultParams,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
dialogVisible,
|
||||
dialogTitle,
|
||||
comName,
|
||||
rowData,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange,
|
||||
toggleVisible,
|
||||
getList,
|
||||
searchSubmit,
|
||||
resetSubmit,
|
||||
open,
|
||||
refreshTable,
|
||||
dels
|
||||
} = useWork({
|
||||
listFun: getExampleListApi,
|
||||
delFun: delsExampApi
|
||||
})
|
||||
|
||||
const searchData = [
|
||||
{
|
||||
label: '标题',
|
||||
value: '',
|
||||
itemType: 'input',
|
||||
field: 'title',
|
||||
placeholder: '请输入标题',
|
||||
clearable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'title',
|
||||
label: '标题',
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: '作者'
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: '创建时间'
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: '重要性',
|
||||
slots: {
|
||||
default: 'importance'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: '阅读数'
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
width: '220px',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
getList()
|
||||
</script>
|
||||
@@ -1,17 +0,0 @@
|
||||
import fetch from '@/axios-config'
|
||||
|
||||
export const getExampleListApi = ({ params }: any) => {
|
||||
return fetch({ url: '/example/list2', method: 'get', params })
|
||||
}
|
||||
|
||||
export const delsExampApi = ({ data }: any) => {
|
||||
return fetch({ url: '/example/delete', method: 'post', data })
|
||||
}
|
||||
|
||||
export const setExampApi = ({ data }: any) => {
|
||||
return fetch({ url: '/example/save', method: 'post', data })
|
||||
}
|
||||
|
||||
export const getExampDetApi = ({ params }: any) => {
|
||||
return fetch({ url: '/example/detail', method: 'get', params })
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<com-detail :data="form" :schema="fromSchema" :collapsed="false" title="文章详情">
|
||||
<template #contentContent="scope">
|
||||
<div v-html="scope.row.content"></div>
|
||||
</template>
|
||||
</com-detail>
|
||||
<div class="dialong__button--wrap">
|
||||
<el-button @click="close">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Detail">
|
||||
import { PropType, reactive } from 'vue'
|
||||
import { getExampDetApi } from '../api'
|
||||
import { useRouter } from 'vue-router'
|
||||
const { push } = useRouter()
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const fromSchema = [
|
||||
{
|
||||
field: 'title',
|
||||
label: '标题',
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: '作者'
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: '创建时间'
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: '重要性'
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: '阅读数'
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: '内容',
|
||||
span: 24,
|
||||
slots: {
|
||||
default: 'content'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const form = reactive<IObj>({
|
||||
id: '', // id
|
||||
author: '', // 作者
|
||||
title: '', // 标题
|
||||
content: '', // 内容
|
||||
importance: '', // 重要性
|
||||
display_time: '', // 创建时间
|
||||
pageviews: 0 // 阅读数
|
||||
})
|
||||
|
||||
async function getDet() {
|
||||
if (props.id) {
|
||||
const id = props.id
|
||||
try {
|
||||
const res: any = await getExampDetApi({
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
if (res) {
|
||||
for (const key in form) {
|
||||
if (key === 'importance') {
|
||||
form[key] = (res.data[key] as number).toString()
|
||||
} else {
|
||||
form[key] = res.data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
push('/example-demo/example-page')
|
||||
}
|
||||
|
||||
getDet()
|
||||
</script>
|
||||
@@ -1,160 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="title" label="标题">
|
||||
<el-input v-model="form.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="author" label="作者">
|
||||
<el-input v-model="form.author" placeholder="请输入作者" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="display_time" label="创建时间">
|
||||
<el-date-picker
|
||||
v-model="form.display_time"
|
||||
type="datetime"
|
||||
placeholder="请选择创建时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="importance" label="重要性">
|
||||
<el-select v-model="form.importance" placeholder="请选择重要性" style="width: 100%">
|
||||
<el-option label="重要" value="3" />
|
||||
<el-option label="良好" value="2" />
|
||||
<el-option label="一般" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="pageviews" label="阅读数">
|
||||
<el-input-number
|
||||
v-model="form.pageviews"
|
||||
:min="0"
|
||||
:max="99999999"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="content" label="内容">
|
||||
<editor ref="editorRef" :value="form.content" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="dialong__button--wrap">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button :loading="subLoading" type="primary" @click="setListData">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="InfoWrite">
|
||||
import { PropType, ref, reactive } from 'vue'
|
||||
import { setExampApi, getExampDetApi } from '../api'
|
||||
import Editor from '_c/Editor/index.vue'
|
||||
import { Message } from '_c/Message'
|
||||
import { useRouter } from 'vue-router'
|
||||
const { push } = useRouter()
|
||||
|
||||
const requiredRule: {
|
||||
required: boolean
|
||||
message: string
|
||||
} = {
|
||||
required: true,
|
||||
message: '该项为必填项'
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
const editorRef = ref<Nullable<HTMLElement>>(null)
|
||||
const formRef = ref<Nullable<HTMLElement>>(null)
|
||||
|
||||
const subLoading = ref<boolean>(false)
|
||||
const form = reactive<IObj>({
|
||||
id: '', // id
|
||||
author: '', // 作者
|
||||
title: '', // 标题
|
||||
content: '', // 内容
|
||||
importance: '', // 重要性
|
||||
display_time: '', // 创建时间
|
||||
pageviews: 0 // 阅读数
|
||||
})
|
||||
const rules = reactive<IObj>({
|
||||
title: [requiredRule],
|
||||
author: [requiredRule],
|
||||
content: [requiredRule],
|
||||
importance: [requiredRule],
|
||||
display_time: [requiredRule],
|
||||
pageviews: [requiredRule]
|
||||
})
|
||||
|
||||
async function getDet() {
|
||||
if (props.id) {
|
||||
const id = props.id
|
||||
try {
|
||||
const res: any = await getExampDetApi({
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
if (res) {
|
||||
for (const key in form) {
|
||||
if (key === 'importance') {
|
||||
form[key] = (res.data[key] as number).toString()
|
||||
} else {
|
||||
form[key] = res.data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增或者编辑
|
||||
function setListData() {
|
||||
try {
|
||||
subLoading.value = true
|
||||
form.content = (editorRef.value as any).getHtml()
|
||||
;(formRef.value as any).validate(async (valid) => {
|
||||
if (valid) {
|
||||
const res = await setExampApi({
|
||||
data: form
|
||||
})
|
||||
if (res) {
|
||||
Message.success(form.id ? '编辑成功' : '新增成功')
|
||||
emit('success', form.id ? 'edit' : 'add')
|
||||
}
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
subLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
push('/example-demo/example-page')
|
||||
}
|
||||
|
||||
getDet()
|
||||
</script>
|
||||
@@ -1,18 +0,0 @@
|
||||
export interface InfoWriteParams {
|
||||
title: string
|
||||
id?: string
|
||||
author: string
|
||||
content: string
|
||||
importance: string
|
||||
display_time: string
|
||||
pageviews: number
|
||||
}
|
||||
|
||||
export interface InfoWriteRules {
|
||||
title?: any[]
|
||||
author?: any[]
|
||||
content?: any[]
|
||||
importance?: any[]
|
||||
display_time?: any[]
|
||||
pageviews?: any[]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<template>
|
||||
<info-write @success="success" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ExampleAdd">
|
||||
import InfoWrite from './components/InfoWrite.vue'
|
||||
import bus from '@/vue-bus'
|
||||
function success(type: string) {
|
||||
bus.$emit('success', type)
|
||||
}
|
||||
</script>
|
||||
@@ -1,10 +0,0 @@
|
||||
<template>
|
||||
<detail :id="id" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ExampleDetail">
|
||||
import Detail from './components/Detail.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
const { query } = useRoute()
|
||||
const id = query.id as string
|
||||
</script>
|
||||
@@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<info-write :id="id" @success="success" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import InfoWrite from './components/InfoWrite.vue'
|
||||
import bus from '@/vue-bus'
|
||||
import { useRoute } from 'vue-router'
|
||||
const { query } = useRoute()
|
||||
const id = query.id as string
|
||||
|
||||
// 成功之后的回调
|
||||
function success(type: string) {
|
||||
// 由于使用的是页面跳转,所以只能通过vueBus去进行通信
|
||||
bus.$emit('success', type)
|
||||
}
|
||||
</script>
|
||||
@@ -1,139 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="search__example--wrap">
|
||||
<com-search :data="searchData" @search-submit="searchSubmit" @reset-submit="resetSubmit" />
|
||||
</div>
|
||||
|
||||
<div class="button__example--wrap">
|
||||
<el-button type="primary" icon="el-icon-circle-plus-outline" @click="open(null)">
|
||||
新增
|
||||
</el-button>
|
||||
<el-button type="danger" icon="el-icon-delete" @click="dels">删除</el-button>
|
||||
</div>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
selection
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
currentPage: defaultParams.pageIndex,
|
||||
total: total,
|
||||
onSizeChange: handleSizeChange,
|
||||
onCurrentChange: handleCurrentChange
|
||||
}"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<template #importance="scope">
|
||||
<el-tag
|
||||
:type="
|
||||
scope.row.importance === 3
|
||||
? 'success'
|
||||
: scope.row.importance === 2
|
||||
? 'warning'
|
||||
: 'danger'
|
||||
"
|
||||
>
|
||||
{{ scope.row.importance === 3 ? '重要' : scope.row.importance === 2 ? '良好' : '一般' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #action="scope">
|
||||
<el-button type="primary" size="mini" @click="open(scope.row)">编辑</el-button>
|
||||
<el-button type="success" size="mini" @click="open(scope.row, 'Detail')">查看</el-button>
|
||||
<el-button type="danger" size="mini" @click="dels(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ExampleDialog">
|
||||
import { onBeforeUnmount } from 'vue'
|
||||
import { getExampleListApi, delsExampApi } from './api'
|
||||
import { useWork } from '@/hooks/work/useWork'
|
||||
import { useRouter } from 'vue-router'
|
||||
const { push } = useRouter()
|
||||
import bus from '@/vue-bus'
|
||||
const {
|
||||
defaultParams,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange,
|
||||
getList,
|
||||
searchSubmit,
|
||||
resetSubmit,
|
||||
refreshTable,
|
||||
dels
|
||||
} = useWork({
|
||||
listFun: getExampleListApi,
|
||||
delFun: delsExampApi
|
||||
})
|
||||
|
||||
const searchData = [
|
||||
{
|
||||
label: '标题',
|
||||
value: '',
|
||||
itemType: 'input',
|
||||
field: 'title',
|
||||
placeholder: '请输入标题',
|
||||
clearable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'title',
|
||||
label: '标题',
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: '作者'
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: '创建时间'
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: '重要性',
|
||||
slots: {
|
||||
default: 'importance'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: '阅读数'
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
width: '220px',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
function open(row: Nullable<IObj>, component?: string) {
|
||||
push(
|
||||
!row
|
||||
? `/example-demo/example-add`
|
||||
: component
|
||||
? `/example-demo/example-detail?id=${row.id}`
|
||||
: `/example-demo/example-edit?id=${row.id}`
|
||||
)
|
||||
}
|
||||
|
||||
getList()
|
||||
|
||||
bus.$on('success', (type: string) => {
|
||||
refreshTable(type)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
bus.$off('success')
|
||||
})
|
||||
</script>
|
||||
@@ -1,32 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title=" 引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。引导页基于 intro.js"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-button type="primary" @click.prevent.stop="guide"> 开始引导 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Guide">
|
||||
import { onMounted } from 'vue'
|
||||
import { useIntro } from '@/hooks/web/useIntro'
|
||||
const { intro } = useIntro()
|
||||
import steps from './steps'
|
||||
|
||||
function guide() {
|
||||
intro.start()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
intro.addSteps(steps as any[]).setOptions({
|
||||
prevLabel: '上一步',
|
||||
nextLabel: '下一步',
|
||||
skipLabel: '跳过',
|
||||
doneLabel: '结束'
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@@ -1,40 +0,0 @@
|
||||
const steps = [
|
||||
{
|
||||
element: '#sidebar__wrap',
|
||||
title: '菜单栏',
|
||||
intro: '以路由的结构渲染的菜单栏',
|
||||
position: 'right'
|
||||
},
|
||||
{
|
||||
element: '#hamburger-container',
|
||||
title: '展开缩收',
|
||||
intro: '用于展开和缩放菜单栏',
|
||||
position: 'bottom'
|
||||
},
|
||||
{
|
||||
element: '#breadcrumb-container',
|
||||
title: '面包屑',
|
||||
intro: '用于记录当前路由结构',
|
||||
position: 'bottom'
|
||||
},
|
||||
{
|
||||
element: '#screenfull-container',
|
||||
title: '是否全屏',
|
||||
intro: '用于设置是否全屏',
|
||||
position: 'bottom'
|
||||
},
|
||||
{
|
||||
element: '#user-container',
|
||||
title: '用户信息',
|
||||
intro: '用于展示用户',
|
||||
position: 'bottom'
|
||||
},
|
||||
{
|
||||
element: '#tag-container',
|
||||
title: '标签页',
|
||||
intro: '用于记录路由历史记录',
|
||||
position: 'bottom'
|
||||
}
|
||||
]
|
||||
|
||||
export default steps
|
||||
@@ -1,58 +0,0 @@
|
||||
<template>
|
||||
<div class="icons-container">
|
||||
<div v-for="item of svgIcons" :key="item" v-clipboard="generateIconCode(item)">
|
||||
<el-tooltip placement="top" :content="generateIconCode(item)">
|
||||
<div class="icon-item">
|
||||
<svg-icon :icon-class="item" class-name="disabled" />
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import svgIcons from './svg-icons'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'Icons',
|
||||
setup() {
|
||||
function generateIconCode(symbol: string) {
|
||||
return `<svg-icon icon-class="${symbol}" />`
|
||||
}
|
||||
return {
|
||||
svgIcons,
|
||||
generateIconCode
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.icons-container {
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
|
||||
.icon-item {
|
||||
float: left;
|
||||
width: 100px;
|
||||
height: 85px;
|
||||
margin: 20px;
|
||||
font-size: 30px;
|
||||
color: #24292e;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +0,0 @@
|
||||
const modules = import.meta.glob('../../assets/icons/*.svg')
|
||||
|
||||
const svgIcons: string[] = []
|
||||
|
||||
for (const key in modules) {
|
||||
svgIcons.push(key.split('../../assets/icons/')[1].split('.')[0])
|
||||
}
|
||||
|
||||
export default svgIcons
|
||||
@@ -1,9 +0,0 @@
|
||||
import fetch from '@/axios-config'
|
||||
|
||||
export const loginApi = ({ data }: FetchConfig) => {
|
||||
return fetch({ url: '/user/login', method: 'post', data })
|
||||
}
|
||||
|
||||
export const getRoleDetApi = ({ params }: FetchConfig) => {
|
||||
return fetch({ url: '/role/detail', method: 'get', params })
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { loginApi, getRoleDetApi } from './api'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
const { wsCache } = useCache()
|
||||
|
||||
@@ -50,25 +49,14 @@ export default defineComponent({
|
||||
try {
|
||||
loading.value = true
|
||||
// 模拟登录接口之后返回角色信息
|
||||
const res: IObj = await loginApi({ data: form })
|
||||
if (res) {
|
||||
// 获取权限信息
|
||||
const role = await getRoleDetApi({
|
||||
params: {
|
||||
id: res.data.roleId
|
||||
}
|
||||
wsCache.set(appStore.getUserInfo, form)
|
||||
permissionStore.generateRoutes().then(() => {
|
||||
permissionStore.getAddRouters.forEach(async (route) => {
|
||||
await addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
||||
})
|
||||
if (role) {
|
||||
wsCache.set(appStore.getUserInfo, Object.assign(form, role.data))
|
||||
permissionStore.generateRoutes().then(() => {
|
||||
permissionStore.getAddRouters.forEach(async (route) => {
|
||||
await addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
||||
})
|
||||
permissionStore.setIsAddRouters(true)
|
||||
push({ path: redirect.value || '/' })
|
||||
})
|
||||
}
|
||||
}
|
||||
permissionStore.setIsAddRouters(true)
|
||||
push({ path: redirect.value || '/' })
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import fetch from '@/axios-config'
|
||||
|
||||
export const getRoleListApi = ({ params }: any) => {
|
||||
return fetch({ url: '/role/list', method: 'get', params })
|
||||
}
|
||||
|
||||
export const setRoleApi = ({ data }: any) => {
|
||||
return fetch({ url: '/role/save', method: 'post', data })
|
||||
}
|
||||
|
||||
export const getRoleDetApi = ({ params }: any) => {
|
||||
return fetch({ url: '/role/detail', method: 'get', params })
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="roleName" label="角色名">
|
||||
<el-input v-model="form.roleName" disabled placeholder="请输入角色名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="角色权限">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
:check-strictly="false"
|
||||
:data="routesData as any"
|
||||
:props="defaultProps as any"
|
||||
show-checkbox
|
||||
accordion
|
||||
node-key="path"
|
||||
highlight-current
|
||||
class="permission-tree"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="dialong__button--wrap">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button :loading="subLoading" type="primary" @click="setListData">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="InfoWrite">
|
||||
import { PropType, computed, nextTick, reactive, ref } from 'vue'
|
||||
import path from 'path-browserify'
|
||||
import { setRoleApi, getRoleDetApi } from '../api'
|
||||
import { asyncRouterMap } from '@/router'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import { Message } from '_c/Message'
|
||||
import { AppRouteRecordRaw } from '@/router/types'
|
||||
|
||||
const requiredRule: {
|
||||
required: boolean
|
||||
message: string
|
||||
} = {
|
||||
required: true,
|
||||
message: '该项为必填项'
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<Nullable<IObj>>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['success', 'close'])
|
||||
|
||||
const tree = ref<Nullable<HTMLElement>>(null)
|
||||
const formRef = ref<Nullable<HTMLElement>>(null)
|
||||
const subLoading = ref<boolean>(false)
|
||||
const form = reactive<IObj>({
|
||||
id: '', // id
|
||||
roleName: '', // 角色名
|
||||
checkedNodes: [], // 被选中的节点
|
||||
checkedkeys: [] // 被选中的keys
|
||||
})
|
||||
const rules = reactive<IObj>({
|
||||
roleName: [requiredRule]
|
||||
})
|
||||
const routes = ref<IObj>([])
|
||||
const defaultProps = reactive<IObj>({
|
||||
children: 'children',
|
||||
label: 'title'
|
||||
})
|
||||
|
||||
const routesData = computed(() => routes.value)
|
||||
|
||||
async function getDet() {
|
||||
if (props.info) {
|
||||
const id = props.info.id
|
||||
try {
|
||||
const res: any = await getRoleDetApi({
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
if (res) {
|
||||
for (const key in form) {
|
||||
form[key] = res.data[key]
|
||||
}
|
||||
nextTick(() => {
|
||||
;(tree.value as any).setCheckedKeys(form.checkedkeys)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增或者编辑
|
||||
function setListData() {
|
||||
try {
|
||||
subLoading.value = true
|
||||
;(formRef.value as any).validate(async (valid) => {
|
||||
if (valid) {
|
||||
// 获取所有被选中节点,由于是前端渲染,所以只要保存一维数组就行
|
||||
form.checkedNodes = (tree.value as any).getCheckedNodes(false, true)
|
||||
console.log(JSON.stringify(form.checkedNodes))
|
||||
// 获取所有被选中的keys,便于渲染是否选中
|
||||
form.checkedkeys = (tree.value as any).getCheckedKeys()
|
||||
console.log(JSON.stringify(form.checkedkeys))
|
||||
const res = await setRoleApi({
|
||||
data: form
|
||||
})
|
||||
if (res) {
|
||||
Message.success(
|
||||
form.id ? '编辑成功,请重新退出登录后查看效果' : '新增成功,请重新退出登录后查看效果'
|
||||
)
|
||||
emit('success', form.id ? 'edit' : 'add')
|
||||
}
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
subLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function generateRoutes(routes: AppRouteRecordRaw[], basePath = '/') {
|
||||
const res: AppRouteRecordRaw[] = []
|
||||
|
||||
for (let route of routes) {
|
||||
// skip some route
|
||||
if (route.meta && route.meta.hidden) {
|
||||
continue
|
||||
}
|
||||
|
||||
const onlyOneShowingChild = onlyOneShowingChildFn(
|
||||
route.children,
|
||||
route,
|
||||
path.resolve(basePath, route.path)
|
||||
)
|
||||
|
||||
if (route.children && onlyOneShowingChild && !(route.meta && route.meta.alwaysShow)) {
|
||||
route = onlyOneShowingChild
|
||||
}
|
||||
|
||||
const data = {
|
||||
path: isExternal(route.path) ? route.path : path.resolve(basePath, route.path),
|
||||
title: route.meta && route.meta.title,
|
||||
name: route.name
|
||||
}
|
||||
// recursive child routes
|
||||
if (route.children) {
|
||||
;(data as any).children = generateRoutes(route.children, data.path)
|
||||
}
|
||||
res.push(data as any)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
function onlyOneShowingChildFn(
|
||||
children: AppRouteRecordRaw[] = [],
|
||||
parent: AppRouteRecordRaw,
|
||||
basePath: string
|
||||
) {
|
||||
let onlyOneChild: Nullable<AppRouteRecordRaw | any> = null
|
||||
const showingChildren = children.filter((item) => !(item.meta && item.meta.hidden))
|
||||
// When there is only one child route, the child route is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
onlyOneChild = showingChildren[0]
|
||||
onlyOneChild.path = isExternal(onlyOneChild.path)
|
||||
? onlyOneChild.path
|
||||
: path.resolve(basePath, onlyOneChild.path)
|
||||
return onlyOneChild
|
||||
}
|
||||
|
||||
// Show parent if there are no child route to display
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild = { ...parent, path: '', noShowingChildren: true }
|
||||
return onlyOneChild
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const oldRoutes = [...asyncRouterMap]
|
||||
routes.value = generateRoutes(oldRoutes)
|
||||
getDet()
|
||||
</script>
|
||||
@@ -1,225 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="130px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="roleName" label="角色名">
|
||||
<el-input v-model="form.roleName" disabled placeholder="请输入角色名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="角色权限">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
:check-strictly="false"
|
||||
:expand-on-click-node="false"
|
||||
:data="routesData as any"
|
||||
:props="defaultProps as any"
|
||||
accordion
|
||||
node-key="path"
|
||||
highlight-current
|
||||
class="permission-tree"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="seletTreeData" :span="12">
|
||||
<el-form-item label="title">
|
||||
<el-input v-model="seletTreeData.title" />
|
||||
</el-form-item>
|
||||
<el-form-item label="component">
|
||||
<el-input v-model="seletTreeData.component" />
|
||||
</el-form-item>
|
||||
<el-form-item label="redirect">
|
||||
<el-input v-model="seletTreeData.redirect" />
|
||||
</el-form-item>
|
||||
<el-form-item label="activeMenu">
|
||||
<el-input v-model="seletTreeData.meta.activeMenu" />
|
||||
</el-form-item>
|
||||
<el-form-item label="name">
|
||||
<el-input v-model="seletTreeData.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="icon">
|
||||
<el-input v-model="seletTreeData.meta.icon" />
|
||||
</el-form-item>
|
||||
<el-form-item label="hidden">
|
||||
<el-switch v-model="seletTreeData.meta.hidden" />
|
||||
</el-form-item>
|
||||
<el-form-item label="alwaysShow">
|
||||
<el-switch v-model="seletTreeData.meta.alwaysShow" />
|
||||
</el-form-item>
|
||||
<el-form-item label="noCache">
|
||||
<el-switch v-model="seletTreeData.meta.noCache" />
|
||||
</el-form-item>
|
||||
<el-form-item label="breadcrumb">
|
||||
<el-switch v-model="seletTreeData.meta.breadcrumb" />
|
||||
</el-form-item>
|
||||
<el-form-item label="affix">
|
||||
<el-switch v-model="seletTreeData.meta.affix" />
|
||||
</el-form-item>
|
||||
<el-form-item label="noTagsView">
|
||||
<el-switch v-model="seletTreeData.meta.noTagsView" />
|
||||
</el-form-item>
|
||||
<el-form-item label="showMainRoute">
|
||||
<el-switch v-model="seletTreeData.meta.showMainRoute" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="dialong__button--wrap">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button :loading="subLoading" type="primary" @click="setListData">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="InfoWrite2">
|
||||
import { PropType, computed, nextTick, reactive, ref } from 'vue'
|
||||
import { setRoleApi, getRoleDetApi } from '../api'
|
||||
import { Message } from '_c/Message'
|
||||
import { AppRouteRecordRaw } from '@/router/types'
|
||||
|
||||
const requiredRule: {
|
||||
required: boolean
|
||||
message: string
|
||||
} = {
|
||||
required: true,
|
||||
message: '该项为必填项'
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<Nullable<IObj>>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['success', 'close'])
|
||||
|
||||
const tree = ref<Nullable<HTMLElement>>(null)
|
||||
const formRef = ref<Nullable<HTMLElement>>(null)
|
||||
const subLoading = ref<boolean>(false)
|
||||
const form = reactive<IObj>({
|
||||
id: '', // id
|
||||
roleName: '', // 角色名
|
||||
checkedNodes: [], // 被选中的节点
|
||||
checkedkeys: [] // 被选中的keys
|
||||
})
|
||||
const rules = reactive<IObj>({
|
||||
roleName: [requiredRule]
|
||||
})
|
||||
const routes = ref<IObj>([])
|
||||
const defaultProps = reactive<IObj>({
|
||||
children: 'children',
|
||||
label: 'title'
|
||||
})
|
||||
const seletTreeData = ref<Nullable<IObj>>(null)
|
||||
const routesData = computed(() => routes.value)
|
||||
|
||||
async function getDet() {
|
||||
if (props.info) {
|
||||
const id = props.info.id
|
||||
try {
|
||||
const res: any = await getRoleDetApi({
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
if (res) {
|
||||
console.log(res)
|
||||
for (const key in form) {
|
||||
form[key] = res.data[key]
|
||||
}
|
||||
routes.value = generateRoutes(form.checkedNodes)
|
||||
nextTick(() => {
|
||||
;(tree.value as any).setCheckedKeys(form.checkedkeys)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 树形点击
|
||||
function handleNodeClick(data: IObj) {
|
||||
seletTreeData.value = data
|
||||
}
|
||||
|
||||
// 新增或者编辑
|
||||
function setListData() {
|
||||
try {
|
||||
subLoading.value = true
|
||||
;(formRef.value as any).validate(async (valid) => {
|
||||
if (valid) {
|
||||
console.log(routesData.value)
|
||||
// 获取所有被选中节点
|
||||
// const checkedNodes = this.$refs.tree.getCheckedNodes(false, true).filter(v => {
|
||||
// if (v.path.includes('/')) return v
|
||||
// })
|
||||
// // 获取所有被选中的keys,便于渲染是否选中
|
||||
// this.form.checkedkeys = this.$refs.tree.getCheckedKeys()
|
||||
// console.log(JSON.stringify(this.form.checkedkeys))
|
||||
|
||||
// this.form.checkedNodes = this.getFilterNodes(checkedNodes)
|
||||
// console.log(JSON.stringify(this.form.checkedNodes))
|
||||
const res = await setRoleApi({
|
||||
data: Object.assign(form, { checkedNodes: routesData.value })
|
||||
})
|
||||
if (res) {
|
||||
Message.success(
|
||||
form.id ? '编辑成功,请重新退出登录后查看效果' : '新增成功,请重新退出登录后查看效果'
|
||||
)
|
||||
emit('success', form.id ? 'edit' : 'add')
|
||||
}
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
subLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
// 树形渲染过滤
|
||||
function generateRoutes(routes: AppRouteRecordRaw[]) {
|
||||
const res: AppRouteRecordRaw[] = []
|
||||
|
||||
for (const route of routes) {
|
||||
const data: AppRouteRecordRaw = {
|
||||
path: route.path,
|
||||
name: route.name,
|
||||
redirect: route.redirect || '',
|
||||
title: (route as any).title || (route.meta && route.meta.title),
|
||||
component: (route as any).component || '',
|
||||
meta: {
|
||||
title: (route as any).title || (route.meta && route.meta.title),
|
||||
alwaysShow: route.meta && route.meta.alwaysShow,
|
||||
hidden: route.meta && route.meta.hidden,
|
||||
icon: route.meta && route.meta.icon,
|
||||
noCache: route.meta && route.meta.noCache,
|
||||
breadcrumb: route.meta && route.meta.breadcrumb,
|
||||
affix: route.meta && route.meta.affix,
|
||||
noTagsView: route.meta && route.meta.noTagsView,
|
||||
activeMenu: route.meta && route.meta.activeMenu,
|
||||
showMainRoute: route.meta && route.meta.showMainRoute
|
||||
}
|
||||
}
|
||||
// recursive child routes
|
||||
if (route.children) {
|
||||
data.children = generateRoutes(route.children)
|
||||
}
|
||||
res.push(data)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
getDet()
|
||||
</script>
|
||||
@@ -1,124 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="由于是模拟数据,所以只提供了两种不同权限的角色,开发者可根据实际情况自行改造结合。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<div class="search__example--wrap">
|
||||
<com-search :data="searchData" @search-submit="searchSubmit" @reset-submit="resetSubmit" />
|
||||
</div>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
currentPage: defaultParams.pageIndex,
|
||||
total: total,
|
||||
onSizeChange: handleSizeChange,
|
||||
onCurrentChange: handleCurrentChange
|
||||
}"
|
||||
>
|
||||
<template #remark="scope">
|
||||
<span>模拟</span>
|
||||
<el-tag
|
||||
:type="scope.row.roleName === 'admin' ? 'success' : 'warning'"
|
||||
style="margin: 0 15px"
|
||||
>
|
||||
{{ scope.row.roleName === 'admin' ? '前端' : '后端' }}
|
||||
</el-tag>
|
||||
<span>角色</span>
|
||||
</template>
|
||||
|
||||
<template #action="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
@click="open(scope.row, scope.row.roleName === 'admin' ? 'InfoWrite' : 'InfoWrite2')"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
|
||||
<com-dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<info-write
|
||||
v-if="comName === 'InfoWrite' && dialogVisible"
|
||||
:info="rowData"
|
||||
@close="toggleVisible"
|
||||
@success="refreshTable"
|
||||
/>
|
||||
<info-write2
|
||||
v-if="comName === 'InfoWrite2' && dialogVisible"
|
||||
:info="rowData"
|
||||
@close="toggleVisible"
|
||||
@success="refreshTable"
|
||||
/>
|
||||
</com-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Role">
|
||||
import { getRoleListApi } from './api'
|
||||
import { useWork } from '@/hooks/work/useWork'
|
||||
import InfoWrite from './components/InfoWrite.vue'
|
||||
import InfoWrite2 from './components/InfoWrite2.vue'
|
||||
const {
|
||||
defaultParams,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
dialogVisible,
|
||||
dialogTitle,
|
||||
comName,
|
||||
rowData,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
toggleVisible,
|
||||
getList,
|
||||
searchSubmit,
|
||||
resetSubmit,
|
||||
open,
|
||||
refreshTable
|
||||
} = useWork({
|
||||
listFun: getRoleListApi
|
||||
})
|
||||
|
||||
const searchData = [
|
||||
{
|
||||
label: '角色名',
|
||||
value: '',
|
||||
itemType: 'input',
|
||||
field: 'roleName',
|
||||
placeholder: '请输入角色名',
|
||||
clearable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'roleName',
|
||||
label: '角色名'
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
slots: {
|
||||
default: 'remark'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
width: '80px',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
getList()
|
||||
</script>
|
||||
@@ -1,5 +0,0 @@
|
||||
import fetch from '@/axios-config'
|
||||
|
||||
export const getUserListApi = ({ params }: any) => {
|
||||
return fetch({ url: '/user/list', method: 'get', params })
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="由于是模拟数据,所以只提供了两种不同权限的帐号,开发者可根据实际情况自行改造结合。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<div class="search__example--wrap">
|
||||
<com-search :data="searchData" @search-submit="searchSubmit" @reset-submit="resetSubmit" />
|
||||
</div>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
currentPage: defaultParams.pageIndex,
|
||||
total: total,
|
||||
onSizeChange: handleSizeChange,
|
||||
onCurrentChange: handleCurrentChange
|
||||
}"
|
||||
>
|
||||
<template #remark="scope">
|
||||
<span>模拟</span>
|
||||
<el-tag
|
||||
:type="scope.row.userName === 'admin' ? 'success' : 'warning'"
|
||||
style="margin: 0 15px"
|
||||
>
|
||||
{{ scope.row.userName === 'admin' ? '前端' : '后端' }}
|
||||
</el-tag>
|
||||
<span>控制路由权限</span>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="User">
|
||||
import { getUserListApi } from './api'
|
||||
import { useWork } from '@/hooks/work/useWork'
|
||||
const {
|
||||
defaultParams,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
getList,
|
||||
searchSubmit,
|
||||
resetSubmit
|
||||
} = useWork({
|
||||
listFun: getUserListApi
|
||||
})
|
||||
|
||||
const searchData = [
|
||||
{
|
||||
label: '帐号',
|
||||
value: '',
|
||||
itemType: 'input',
|
||||
field: 'userName',
|
||||
placeholder: '请输入帐号',
|
||||
clearable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'userName',
|
||||
label: '帐号'
|
||||
},
|
||||
{
|
||||
field: 'password',
|
||||
label: '密码'
|
||||
},
|
||||
{
|
||||
field: 'role',
|
||||
label: '角色'
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
slots: {
|
||||
default: 'remark'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
getList()
|
||||
</script>
|
||||
@@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 基础表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="BasicTable">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带边框表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData" border />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="BorderTable">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,91 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 自定义表头"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="
|
||||
tableData.filter(
|
||||
(data) => !search || data.name.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #actionHeader>
|
||||
<el-input v-model="search" size="mini" placeholder="输入关键字搜索" />
|
||||
</template>
|
||||
<template #action="scope">
|
||||
<el-button size="mini" @click="handleEdit(scope.$index, scope.row)">Edit</el-button>
|
||||
<el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">
|
||||
Delete
|
||||
</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CustomHeader">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
slots: {
|
||||
header: 'actionHeader',
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎1',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎2',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎3',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎4',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
const search = ref<string>('')
|
||||
|
||||
function handleEdit(index: number, row: any) {
|
||||
console.log(index, row)
|
||||
}
|
||||
function handleDelete(index: number, row: any) {
|
||||
console.log(index, row)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,68 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 自定义索引"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CustomIndex">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
field: 'index',
|
||||
type: 'index',
|
||||
index: (index: number) => {
|
||||
return index * 2
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,125 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 展开行"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table ref="multipleTable" v-loading="loading" :columns="columns" :data="tableData">
|
||||
<template #id="scope">
|
||||
<el-form label-position="left" inline class="demo-table-expand">
|
||||
<el-form-item label="商品名称">
|
||||
<span>{{ scope.row.name }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属店铺">
|
||||
<span>{{ scope.row.shop }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品 ID">
|
||||
<span>{{ scope.row.id }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺 ID">
|
||||
<span>{{ scope.row.shopId }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品分类">
|
||||
<span>{{ scope.row.category }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺地址">
|
||||
<span>{{ scope.row.address }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品描述">
|
||||
<span>{{ scope.row.desc }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ExpandRow">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'id',
|
||||
type: 'expand',
|
||||
slots: {
|
||||
default: 'id'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'id',
|
||||
label: '商品ID'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '商品名称'
|
||||
},
|
||||
{
|
||||
field: 'desc',
|
||||
label: '描述'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
id: '12987122',
|
||||
name: '好滋好味鸡蛋仔',
|
||||
category: '江浙小吃、小吃零食',
|
||||
desc: '荷兰优质淡奶,奶香浓而不腻',
|
||||
address: '上海市普陀区真北路',
|
||||
shop: '王小虎夫妻店',
|
||||
shopId: '10333'
|
||||
},
|
||||
{
|
||||
id: '12987123',
|
||||
name: '好滋好味鸡蛋仔',
|
||||
category: '江浙小吃、小吃零食',
|
||||
desc: '荷兰优质淡奶,奶香浓而不腻',
|
||||
address: '上海市普陀区真北路',
|
||||
shop: '王小虎夫妻店',
|
||||
shopId: '10333'
|
||||
},
|
||||
{
|
||||
id: '12987125',
|
||||
name: '好滋好味鸡蛋仔',
|
||||
category: '江浙小吃、小吃零食',
|
||||
desc: '荷兰优质淡奶,奶香浓而不腻',
|
||||
address: '上海市普陀区真北路',
|
||||
shop: '王小虎夫妻店',
|
||||
shopId: '10333'
|
||||
},
|
||||
{
|
||||
id: '12987126',
|
||||
name: '好滋好味鸡蛋仔',
|
||||
category: '江浙小吃、小吃零食',
|
||||
desc: '荷兰优质淡奶,奶香浓而不腻',
|
||||
address: '上海市普陀区真北路',
|
||||
shop: '王小虎夫妻店',
|
||||
shopId: '10333'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.demo-table-expand) {
|
||||
font-size: 0;
|
||||
|
||||
label {
|
||||
width: 90px;
|
||||
color: #99a9bf;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
width: 50%;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,149 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定列和表头"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
border
|
||||
height="250"
|
||||
style="width: 820px"
|
||||
>
|
||||
<template #action="scope">
|
||||
<el-button type="text" size="small" @click="handleClick(scope.row)">查看</el-button>
|
||||
<el-button type="text" size="small">编辑</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FixedColumnHeader">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期',
|
||||
fixed: true,
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'province',
|
||||
label: '省份',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'city',
|
||||
label: '市区',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址',
|
||||
width: '300'
|
||||
},
|
||||
{
|
||||
field: 'zip',
|
||||
label: '邮编',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
width: '100',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function handleClick(row: any) {
|
||||
console.log(row)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,110 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定列"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData" border style="width: 820px">
|
||||
<template #action="scope">
|
||||
<el-button type="text" size="small" @click="handleClick(scope.row)">查看</el-button>
|
||||
<el-button type="text" size="small">编辑</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FixedColumn">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期',
|
||||
fixed: true,
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'province',
|
||||
label: '省份',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'city',
|
||||
label: '市区',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址',
|
||||
width: '300'
|
||||
},
|
||||
{
|
||||
field: 'zip',
|
||||
label: '邮编',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
width: '100',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function handleClick(row: any) {
|
||||
console.log(row)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定表头"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData" height="250" border />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FixedHeader">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,147 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 流体高度"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
border
|
||||
max-height="250"
|
||||
style="width: 820px"
|
||||
>
|
||||
<template #action="scope">
|
||||
<el-button type="text" size="small" @click="deleteRow(scope.$index)">移除</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="FluidHeight">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期',
|
||||
fixed: true,
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'province',
|
||||
label: '省份',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'city',
|
||||
label: '市区',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址',
|
||||
width: '300'
|
||||
},
|
||||
{
|
||||
field: 'zip',
|
||||
label: '邮编',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
width: '100',
|
||||
fixed: 'right',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
const tableData = ref<IObj[]>([
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
zip: 200333
|
||||
}
|
||||
])
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function deleteRow(index: number) {
|
||||
tableData.value.splice(index, 1)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,151 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 合并行或列"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:span-method="arraySpanMethod"
|
||||
border
|
||||
/>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns1"
|
||||
:data="tableData"
|
||||
:span-method="objectSpanMethod"
|
||||
border
|
||||
style="margin-top: 20px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="MergeTable">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'id',
|
||||
label: 'ID'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'amount1',
|
||||
label: '数值1',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'amount2',
|
||||
label: '数值2',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'amount3',
|
||||
label: '数值4',
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns1 = [
|
||||
{
|
||||
field: 'id',
|
||||
label: 'ID'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'amount1',
|
||||
label: '数值1(元)'
|
||||
},
|
||||
{
|
||||
field: 'amount2',
|
||||
label: '数值2(元)'
|
||||
},
|
||||
{
|
||||
field: 'amount3',
|
||||
label: '数值4(元)'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
id: '12987122',
|
||||
name: '王小虎',
|
||||
amount1: '234',
|
||||
amount2: '3.2',
|
||||
amount3: 10
|
||||
},
|
||||
{
|
||||
id: '12987123',
|
||||
name: '王小虎',
|
||||
amount1: '165',
|
||||
amount2: '4.43',
|
||||
amount3: 12
|
||||
},
|
||||
{
|
||||
id: '12987124',
|
||||
name: '王小虎',
|
||||
amount1: '324',
|
||||
amount2: '1.9',
|
||||
amount3: 9
|
||||
},
|
||||
{
|
||||
id: '12987125',
|
||||
name: '王小虎',
|
||||
amount1: '621',
|
||||
amount2: '2.2',
|
||||
amount3: 17
|
||||
},
|
||||
{
|
||||
id: '12987126',
|
||||
name: '王小虎',
|
||||
amount1: '539',
|
||||
amount2: '4.1',
|
||||
amount3: 15
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function arraySpanMethod({ rowIndex, columnIndex }: any) {
|
||||
if (rowIndex % 2 === 0) {
|
||||
if (columnIndex === 0) {
|
||||
return [1, 2]
|
||||
} else if (columnIndex === 1) {
|
||||
return [0, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function objectSpanMethod({ rowIndex, columnIndex }: any) {
|
||||
if (columnIndex === 0) {
|
||||
if (rowIndex % 2 === 0) {
|
||||
return {
|
||||
rowspan: 2,
|
||||
colspan: 1
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
rowspan: 0,
|
||||
colspan: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,145 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 多级表头"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData">
|
||||
<template #address="scope"> 地址是: {{ scope.row.address }} </template>
|
||||
<template #action="scope">
|
||||
<el-button type="text" size="small" @click="deleteRow(scope.$index)">移除</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="MultiHeader">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期',
|
||||
fixed: true,
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
label: '配送信息',
|
||||
children: [
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
label: '地址',
|
||||
children: [
|
||||
{
|
||||
field: 'province',
|
||||
label: '省份',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'city',
|
||||
label: '市区',
|
||||
width: '120'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址',
|
||||
slots: {
|
||||
default: 'address'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'zip',
|
||||
label: '邮编',
|
||||
width: '120'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
width: '100',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = ref<any[]>([
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-08',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-06',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
},
|
||||
{
|
||||
date: '2016-05-07',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
}
|
||||
])
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function deleteRow(index: number) {
|
||||
tableData.value.splice(index, 1)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,90 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 多选"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
selection
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
@selection-change="handleSelectionChange"
|
||||
/>
|
||||
|
||||
<div style="margin-top: 20px">
|
||||
<el-button @click="toggleSelection([tableData[1], tableData[2]])">
|
||||
切换第二、第三行的选中状态
|
||||
</el-button>
|
||||
<el-button @click="toggleSelection()">取消选择</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="MultipleChoice">
|
||||
import { ref, unref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
const multipleTable = ref<HTMLElement | null>(null)
|
||||
function toggleSelection(rows?: any[]) {
|
||||
const multipleTableRef = unref(multipleTable as any).getTableRef()
|
||||
if (rows) {
|
||||
rows.forEach((row) => {
|
||||
multipleTableRef.toggleRowSelection(row)
|
||||
})
|
||||
} else {
|
||||
multipleTableRef.clearSelection()
|
||||
}
|
||||
}
|
||||
function handleSelectionChange(val: any) {
|
||||
console.log(val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,79 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 分页表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
currentPage: 1,
|
||||
total: 400,
|
||||
onSizeChange: handleSizeChange,
|
||||
onCurrentChange: handleCurrentChange
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PageTable">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function handleSizeChange(val: number) {
|
||||
console.log(val)
|
||||
}
|
||||
|
||||
function handleCurrentChange(val: number) {
|
||||
console.log(val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,124 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 筛选"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-button @click="resetDateFilter">清除日期过滤器</el-button>
|
||||
<el-button @click="clearFilter">清除所有过滤器</el-button>
|
||||
<com-table
|
||||
ref="filterTable"
|
||||
v-loading="loading"
|
||||
row-key="date"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
>
|
||||
<template #tag="scope">
|
||||
<el-tag
|
||||
:type="(scope.row.tag === '家' ? 'primary' : 'success') as any"
|
||||
disable-transitions
|
||||
>{{ scope.row.tag }}</el-tag
|
||||
>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ScreenTable">
|
||||
import { ref, unref } from 'vue'
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
tag: '家'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
tag: '公司'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
tag: '家'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
tag: '公司'
|
||||
}
|
||||
]
|
||||
|
||||
const filterTable = ref<HTMLElement | null>(null)
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期',
|
||||
sortable: true,
|
||||
width: '180',
|
||||
columnKey: 'date',
|
||||
filters: [
|
||||
{ text: '2016-05-01', value: '2016-05-01' },
|
||||
{ text: '2016-05-02', value: '2016-05-02' },
|
||||
{ text: '2016-05-03', value: '2016-05-03' },
|
||||
{ text: '2016-05-04', value: '2016-05-04' }
|
||||
],
|
||||
filterMethod: filterHandler
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
},
|
||||
{
|
||||
field: 'tag',
|
||||
label: '标签',
|
||||
filters: [
|
||||
{ text: '家', value: '家' },
|
||||
{ text: '公司', value: '公司' }
|
||||
],
|
||||
filterMethod: filterTag,
|
||||
filterPlacement: 'bottom-end',
|
||||
slots: {
|
||||
default: 'tag'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
function resetDateFilter() {
|
||||
const filterTableRef = unref(filterTable as any).getTableRef()
|
||||
filterTableRef.clearFilter('date')
|
||||
}
|
||||
function clearFilter() {
|
||||
const filterTableRef = unref(filterTable as any).getTableRef()
|
||||
filterTableRef.clearFilter()
|
||||
}
|
||||
function filterTag(value: string, row: any) {
|
||||
return row.tag === value
|
||||
}
|
||||
function filterHandler(value: string, row: any, column: any) {
|
||||
const property = column['property']
|
||||
return row[property] === value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,82 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 单选"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
ref="singleTable"
|
||||
v-loading="loading"
|
||||
highlight-current-row
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
|
||||
<div style="margin-top: 20px">
|
||||
<el-button @click="setCurrent(tableData[1])">选中第二行</el-button>
|
||||
<el-button @click="setCurrent()">取消选择</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="SingleChoice">
|
||||
import { ref, unref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
const singleTable = ref<HTMLElement | null>(null)
|
||||
function setCurrent(row?: any) {
|
||||
const singleTableRef = unref(singleTable as any).getTableRef()
|
||||
singleTableRef.setCurrentRow(row)
|
||||
}
|
||||
function handleCurrentChange(val: any) {
|
||||
console.log(val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 排序"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="SortTable">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,85 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带状态表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:row-class-name="tableRowClassName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="StateTable">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function tableRowClassName({ rowIndex }: any) {
|
||||
if (rowIndex === 1) {
|
||||
return 'warning-row'
|
||||
} else if (rowIndex === 3) {
|
||||
return 'success-row'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.el-table) {
|
||||
.warning-row {
|
||||
background: oldlace;
|
||||
}
|
||||
|
||||
.success-row {
|
||||
background: #f0f9eb;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带斑马纹表格"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData" stripe />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="StripeTable">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,148 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 表尾合计行"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table v-loading="loading" :columns="columns" :data="tableData" border show-summary />
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns1"
|
||||
:data="tableData"
|
||||
border
|
||||
height="200"
|
||||
:summary-method="getSummaries"
|
||||
show-summary
|
||||
style="margin-top: 20px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TotalTable">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'id',
|
||||
label: 'ID'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'amount1',
|
||||
label: '数值1',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'amount2',
|
||||
label: '数值2',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'amount3',
|
||||
label: '数值4',
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns1 = [
|
||||
{
|
||||
field: 'id',
|
||||
label: 'ID'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'amount1',
|
||||
label: '数值1(元)'
|
||||
},
|
||||
{
|
||||
field: 'amount2',
|
||||
label: '数值2(元)'
|
||||
},
|
||||
{
|
||||
field: 'amount3',
|
||||
label: '数值4(元)'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
id: '12987122',
|
||||
name: '王小虎',
|
||||
amount1: '234',
|
||||
amount2: '3.2',
|
||||
amount3: 10
|
||||
},
|
||||
{
|
||||
id: '12987123',
|
||||
name: '王小虎',
|
||||
amount1: '165',
|
||||
amount2: '4.43',
|
||||
amount3: 12
|
||||
},
|
||||
{
|
||||
id: '12987124',
|
||||
name: '王小虎',
|
||||
amount1: '324',
|
||||
amount2: '1.9',
|
||||
amount3: 9
|
||||
},
|
||||
{
|
||||
id: '12987125',
|
||||
name: '王小虎',
|
||||
amount1: '621',
|
||||
amount2: '2.2',
|
||||
amount3: 17
|
||||
},
|
||||
{
|
||||
id: '12987126',
|
||||
name: '王小虎',
|
||||
amount1: '539',
|
||||
amount2: '4.1',
|
||||
amount3: 15
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function getSummaries(param: any) {
|
||||
const { columns, data } = param
|
||||
const sums: any[] = []
|
||||
columns.forEach((column: any, index: number) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '总价'
|
||||
return
|
||||
}
|
||||
const values = data.map((item: any) => Number(item[column.property]))
|
||||
if (!values.every((value: number) => isNaN(value))) {
|
||||
sums[index] = values.reduce((prev: number, curr: number) => {
|
||||
const value = Number(curr)
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr
|
||||
} else {
|
||||
return prev
|
||||
}
|
||||
}, 0)
|
||||
sums[index] += ' 元'
|
||||
} else {
|
||||
sums[index] = 'N/A'
|
||||
}
|
||||
})
|
||||
|
||||
return sums
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,163 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 树形数据与懒加载"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
row-key="id"
|
||||
border
|
||||
default-expand-all
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
/>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns1"
|
||||
:data="tableData1"
|
||||
row-key="id"
|
||||
border
|
||||
lazy
|
||||
:load="load"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
style="margin-top: 20px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="TreeAndLoad">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const columns1 = [
|
||||
{
|
||||
field: 'date',
|
||||
label: '日期'
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名'
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
id: 1,
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
children: [
|
||||
{
|
||||
id: 31,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const tableData1 = [
|
||||
{
|
||||
id: 1,
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄'
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref<boolean>(true)
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
|
||||
function load(_: any, __: any, resolve: Function) {
|
||||
setTimeout(() => {
|
||||
resolve([
|
||||
{
|
||||
id: 31,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄'
|
||||
}
|
||||
])
|
||||
}, 1000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,13 +0,0 @@
|
||||
// 通过mitt实现vue-bus通信
|
||||
|
||||
import mitt from 'mitt'
|
||||
|
||||
const bus: any = {}
|
||||
|
||||
const emitter = mitt()
|
||||
|
||||
bus.$on = emitter.on
|
||||
bus.$off = emitter.off
|
||||
bus.$emit = emitter.emit
|
||||
|
||||
export default bus
|
||||
Reference in New Issue
Block a user