wip: Table component developing
This commit is contained in:
parent
9b4b317817
commit
7b7fcfef59
|
@ -0,0 +1,65 @@
|
|||
import { config } from '@/config/axios/config'
|
||||
import { MockMethod } from 'vite-plugin-mock'
|
||||
import { toAnyString } from '@/utils'
|
||||
import Mock from 'mockjs'
|
||||
|
||||
const { result_code } = config
|
||||
|
||||
const timeout = 1000
|
||||
|
||||
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>'
|
||||
|
||||
const List: {
|
||||
id: string
|
||||
author: string
|
||||
title: string
|
||||
content: string
|
||||
importance: number
|
||||
display_time: string
|
||||
pageviews: number
|
||||
}[] = []
|
||||
|
||||
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: '/example/list',
|
||||
method: 'get',
|
||||
timeout,
|
||||
response: ({ query }) => {
|
||||
const { title, pageIndex, pageSize } = 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: result_code,
|
||||
data: {
|
||||
total: mockList.length,
|
||||
list: pageList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
] as MockMethod[]
|
30
package.json
30
package.json
|
@ -32,15 +32,15 @@
|
|||
"axios": "^0.25.0",
|
||||
"echarts": "^5.3.0",
|
||||
"echarts-wordcloud": "^2.0.0",
|
||||
"element-plus": "1.3.0-beta.9",
|
||||
"intro.js": "^4.3.0",
|
||||
"element-plus": "2.0.0",
|
||||
"intro.js": "^5.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.10",
|
||||
"pinia": "^2.0.11",
|
||||
"qrcode": "^1.5.0",
|
||||
"qs": "^6.10.3",
|
||||
"vue": "3.2.26",
|
||||
"vue": "3.2.30",
|
||||
"vue-i18n": "9.1.9",
|
||||
"vue-router": "^4.0.12",
|
||||
"vue-types": "^4.1.1",
|
||||
|
@ -49,30 +49,30 @@
|
|||
"devDependencies": {
|
||||
"@commitlint/cli": "^16.1.0",
|
||||
"@commitlint/config-conventional": "^16.0.0",
|
||||
"@iconify/json": "^2.0.30",
|
||||
"@intlify/vite-plugin-vue-i18n": "^3.2.1",
|
||||
"@iconify/json": "^2.0.34",
|
||||
"@intlify/vite-plugin-vue-i18n": "^3.2.2",
|
||||
"@purge-icons/generated": "^0.7.0",
|
||||
"@types/intro.js": "^3.0.2",
|
||||
"@types/lodash-es": "^4.17.5",
|
||||
"@types/node": "^17.0.13",
|
||||
"@types/lodash-es": "^4.17.6",
|
||||
"@types/node": "^17.0.15",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/qrcode": "^1.4.2",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||
"@typescript-eslint/parser": "^5.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
||||
"@typescript-eslint/parser": "^5.10.2",
|
||||
"@vitejs/plugin-vue": "^2.1.0",
|
||||
"@vitejs/plugin-vue-jsx": "^1.3.3",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"commitizen": "^4.2.4",
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-define-config": "^1.2.3",
|
||||
"eslint-define-config": "^1.2.4",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^8.4.0",
|
||||
"eslint-plugin-vue": "^8.4.1",
|
||||
"husky": "^7.0.4",
|
||||
"less": "^4.1.2",
|
||||
"lint-staged": "^12.3.2",
|
||||
"postcss": "^8.4.5",
|
||||
"lint-staged": "^12.3.3",
|
||||
"postcss": "^8.4.6",
|
||||
"postcss-html": "^1.3.0",
|
||||
"postcss-less": "^6.0.0",
|
||||
"prettier": "^2.5.1",
|
||||
|
@ -92,7 +92,7 @@
|
|||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"vite-plugin-windicss": "^1.6.3",
|
||||
"vue-tsc": "^0.31.1",
|
||||
"vue-tsc": "^0.31.2",
|
||||
"windicss": "^3.4.3",
|
||||
"windicss-analysis": "^0.3.5"
|
||||
},
|
||||
|
|
2045
pnpm-lock.yaml
2045
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,11 @@
|
|||
import { useAxios } from '@/hooks/web/useAxios'
|
||||
import type { TableData } from './types'
|
||||
|
||||
const { request } = useAxios()
|
||||
|
||||
export const getTableListApi = ({ params }: AxiosConfig) => {
|
||||
return request<{
|
||||
total: number
|
||||
list: TableData[]
|
||||
}>({ url: '/example/list', method: 'get', params })
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
export type TableData = {
|
||||
id: string
|
||||
author: string
|
||||
title: string
|
||||
content: string
|
||||
importance: number
|
||||
display_time: string
|
||||
pageviews: number
|
||||
}
|
|
@ -39,9 +39,16 @@ const emit = defineEmits(['search', 'reset'])
|
|||
const visible = ref(true)
|
||||
|
||||
const newSchema = computed(() => {
|
||||
let schema: FormSchema[] = []
|
||||
let schema: FormSchema[] = cloneDeep(props.schema)
|
||||
if (props.expand && props.expandField && !unref(visible)) {
|
||||
const index = findIndex(schema, (v: FormSchema) => v.field === props.expandField)
|
||||
if (index > -1) {
|
||||
const length = schema.length
|
||||
schema.splice(index + 1, length)
|
||||
}
|
||||
}
|
||||
if (props.layout === 'inline') {
|
||||
schema = cloneDeep(props.schema).concat([
|
||||
schema = schema.concat([
|
||||
{
|
||||
field: 'action',
|
||||
formItemProps: {
|
||||
|
@ -49,14 +56,6 @@ const newSchema = computed(() => {
|
|||
}
|
||||
}
|
||||
])
|
||||
} else {
|
||||
schema = cloneDeep(props.schema)
|
||||
}
|
||||
if (props.expand && props.expandField && !unref(visible)) {
|
||||
const index = findIndex(schema, (v: FormSchema) => v.field === props.expandField)
|
||||
if (index > -1) {
|
||||
schema.splice(0, index + 1)
|
||||
}
|
||||
}
|
||||
return schema
|
||||
})
|
||||
|
@ -86,6 +85,11 @@ const bottonButtonStyle = computed(() => {
|
|||
textAlign: props.buttomPosition
|
||||
}
|
||||
}) as CSSProperties
|
||||
|
||||
const setVisible = () => {
|
||||
unref(elFormRef)?.resetFields()
|
||||
visible.value = !unref(visible)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -108,7 +112,7 @@ const bottonButtonStyle = computed(() => {
|
|||
<Icon icon="ep:refresh-right" class="mr-5px" />
|
||||
{{ t('common.reset') }}
|
||||
</ElButton>
|
||||
<ElButton v-if="showReset" type="text" @click="visible = !visible">
|
||||
<ElButton v-if="showReset" type="text" @click="setVisible">
|
||||
{{ t(visible ? 'common.shrink' : 'common.expand') }}
|
||||
<Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
|
||||
</ElButton>
|
||||
|
@ -126,7 +130,7 @@ const bottonButtonStyle = computed(() => {
|
|||
<Icon icon="ep:refresh-right" class="mr-5px" />
|
||||
{{ t('common.reset') }}
|
||||
</ElButton>
|
||||
<ElButton v-if="showReset" type="text" @click="visible = !visible">
|
||||
<ElButton v-if="showReset" type="text" @click="setVisible">
|
||||
{{ t(visible ? 'common.shrink' : 'common.expand') }}
|
||||
<Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
|
||||
</ElButton>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import Table from './src/Table.vue'
|
||||
|
||||
export { Table }
|
|
@ -0,0 +1,126 @@
|
|||
<script lang="tsx">
|
||||
import { ElTable, ElTableColumn } from 'element-plus'
|
||||
import { defineComponent, PropType, ref, computed, unref } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { setIndex } from './helper'
|
||||
import { getSlot } from '@/utils/tsxHelper'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Table',
|
||||
props: {
|
||||
// 是否多选
|
||||
selection: propTypes.bool.def(true),
|
||||
// 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip,
|
||||
showOverflowTooltip: propTypes.bool.def(true),
|
||||
// 表头
|
||||
columns: {
|
||||
type: Array as PropType<TableColumn[]>,
|
||||
default: () => []
|
||||
},
|
||||
// 是否展示分页
|
||||
// pagination: {
|
||||
// type: [Boolean, Object] as PropType<boolean | IObj>,
|
||||
// default: false
|
||||
// },
|
||||
// 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key)
|
||||
reserveSelection: propTypes.bool.def(false),
|
||||
// 加载状态
|
||||
loading: propTypes.bool.def(false),
|
||||
// 是否叠加索引
|
||||
reserveIndex: propTypes.bool.def(true),
|
||||
// 对齐方式
|
||||
align: propTypes.string
|
||||
.validate((v: string) => ['left', 'center', 'right'].includes(v))
|
||||
.def('left'),
|
||||
headerAlign: propTypes.string
|
||||
.validate((v: string) => ['left', 'center', 'right'].includes(v))
|
||||
.def('left'),
|
||||
// 表头对齐方式
|
||||
data: {
|
||||
type: Array as PropType<Recordable[]>,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
setup(props, { attrs, slots }) {
|
||||
const tableRef = ref<ComponentRef<typeof ElTable>>()
|
||||
|
||||
const getProps = computed(() => props)
|
||||
|
||||
const getBindValue = computed(() => {
|
||||
const bindValue: Recordable = { ...attrs, ...props }
|
||||
delete bindValue.columns
|
||||
delete bindValue.data
|
||||
return bindValue
|
||||
})
|
||||
|
||||
const renderTableSelection = () => {
|
||||
return (
|
||||
<ElTableColumn
|
||||
type="selection"
|
||||
reserveSelection={props.reserveSelection}
|
||||
align={unref(getProps).align}
|
||||
headerAlign={unref(getProps).headerAlign}
|
||||
width="50"
|
||||
></ElTableColumn>
|
||||
)
|
||||
}
|
||||
|
||||
const rnderTableColumn = (columns: TableColumn[]) => {
|
||||
return (props.selection ? [renderTableSelection()] : []).concat(
|
||||
columns.map((v, i) => {
|
||||
if (v.type === 'index') {
|
||||
return (
|
||||
<ElTableColumn
|
||||
type="index"
|
||||
index={v.index ? v.index : setIndex()}
|
||||
align={v.align || unref(getProps).align}
|
||||
headerAlign={v.headerAlign || unref(getProps).headerAlign}
|
||||
label={v.label}
|
||||
width="100px"
|
||||
></ElTableColumn>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<ElTableColumn
|
||||
showOverflowTooltip={unref(getProps).showOverflowTooltip}
|
||||
align={unref(getProps).align}
|
||||
headerAlign={unref(getProps).headerAlign}
|
||||
{...v}
|
||||
prop={v.field}
|
||||
>
|
||||
{{
|
||||
default: () =>
|
||||
// @ts-ignore
|
||||
getSlot(slots, v.field, { row: props.data[i], field: v.field, index: i }) ||
|
||||
v?.formatter?.() ||
|
||||
props.data[i][v.field],
|
||||
// @ts-ignore
|
||||
header: getSlot(slots, `${v.field}-header`)
|
||||
}}
|
||||
</ElTableColumn>
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return () => (
|
||||
<>
|
||||
<ElTable
|
||||
// @ts-ignore
|
||||
ref={tableRef}
|
||||
data={unref(getProps).data}
|
||||
{...getBindValue}
|
||||
v-loading={unref(getProps).loading}
|
||||
>
|
||||
{{
|
||||
default: () => rnderTableColumn(props.columns),
|
||||
// @ts-ignore
|
||||
append: () => getSlot(slots, 'append')
|
||||
}}
|
||||
</ElTable>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,3 @@
|
|||
export const setIndex = () => {
|
||||
return 1
|
||||
}
|
|
@ -100,7 +100,9 @@ export default {
|
|||
infotip: 'Infotip',
|
||||
form: 'Form',
|
||||
defaultForm: 'All examples',
|
||||
search: 'Search'
|
||||
search: 'Search',
|
||||
table: 'Table',
|
||||
defaultTable: 'Basic example'
|
||||
},
|
||||
analysis: {
|
||||
newUser: 'New user',
|
||||
|
@ -301,5 +303,19 @@ export default {
|
|||
left: 'left',
|
||||
center: 'center',
|
||||
right: 'right'
|
||||
},
|
||||
tableDemo: {
|
||||
table: 'Table',
|
||||
tableDes: 'Secondary packaging of Table components based on ElementPlus',
|
||||
index: 'Index',
|
||||
title: 'Title',
|
||||
author: 'Author',
|
||||
displayTime: 'Display time',
|
||||
importance: 'Importance',
|
||||
pageviews: 'Pageviews',
|
||||
action: 'Action',
|
||||
important: 'Important',
|
||||
good: 'Good',
|
||||
commonly: 'Commonly'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,9 @@ export default {
|
|||
infotip: '信息提示',
|
||||
form: '表单',
|
||||
defaultForm: '全部示例',
|
||||
search: '查询'
|
||||
search: '查询',
|
||||
table: '表格',
|
||||
defaultTable: '基础示例'
|
||||
},
|
||||
analysis: {
|
||||
newUser: '新增用户',
|
||||
|
@ -298,5 +300,19 @@ export default {
|
|||
left: '左',
|
||||
center: '中',
|
||||
right: '右'
|
||||
},
|
||||
tableDemo: {
|
||||
table: '表格',
|
||||
tableDes: '基于 ElementPlus 的 Table 组件二次封装',
|
||||
index: '序号',
|
||||
title: '标题',
|
||||
author: '作者',
|
||||
displayTime: '创建时间',
|
||||
importance: '重要性',
|
||||
pageviews: '阅读数',
|
||||
action: '操作',
|
||||
important: '重要',
|
||||
good: '良好',
|
||||
commonly: '一般'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,6 +131,25 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'table',
|
||||
component: getParentLayout(),
|
||||
name: 'TableDemo',
|
||||
meta: {
|
||||
title: t('router.table'),
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'default-table',
|
||||
component: () => import('@/views/Components/Table/DefaultTable.vue'),
|
||||
name: 'DefaultTable',
|
||||
meta: {
|
||||
title: t('router.defaultTable')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
component: () => import('@/views/Components/Search.vue'),
|
||||
|
|
|
@ -95,3 +95,15 @@ export function formatTime(time: Date | number | string, fmt: string) {
|
|||
return fmt
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
*/
|
||||
export function toAnyString() {
|
||||
const str: string = 'xxxxx-xxxxx-4xxxx-yxxxx-xxxxx'.replace(/[xy]/g, (c: string) => {
|
||||
const r: number = (Math.random() * 16) | 0
|
||||
const v: number = c === 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString()
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ const disabledClick = () => {
|
|||
<ElCol :xl="6" :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<ElCard shadow="hover" class="mb-10px text-center">
|
||||
<div class="font-bold">{{ t('qrcodeDemo.size') }}</div>
|
||||
<Qrcode :text="title" :width="250" />
|
||||
<Qrcode :text="title" :width="100" />
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Table } from '@/components/Table'
|
||||
import { getTableListApi } from '@/api/table'
|
||||
import { TableData } from '@/api/table/types'
|
||||
import { ref, h } from 'vue'
|
||||
import { ElTag, ElButton } from 'element-plus'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
field: 'index',
|
||||
label: t('tableDemo.index'),
|
||||
type: 'index'
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
label: t('tableDemo.title')
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: t('tableDemo.author')
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: t('tableDemo.displayTime')
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: t('tableDemo.importance'),
|
||||
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
|
||||
return h(
|
||||
ElTag,
|
||||
{
|
||||
type: cellValue === 1 ? 'success' : cellValue === 1 ? 'warning' : 'danger'
|
||||
},
|
||||
() =>
|
||||
cellValue === 1
|
||||
? t('tableDemo.important')
|
||||
: cellValue === 1
|
||||
? t('tableDemo.good')
|
||||
: t('tableDemo.commonly')
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: t('tableDemo.pageviews')
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: t('tableDemo.action')
|
||||
}
|
||||
]
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
let tableDataList = ref<TableData[]>([])
|
||||
|
||||
const getTableList = async () => {
|
||||
const res = await getTableListApi({
|
||||
params: {
|
||||
pageIndex: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
if (res) {
|
||||
tableDataList.value = res.data.list
|
||||
}
|
||||
}
|
||||
|
||||
getTableList()
|
||||
|
||||
const acitonFn = (data: TableColumnDefault) => {
|
||||
console.log(data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('tableDemo.table')" :message="t('tableDemo.tableDes')">
|
||||
<Table :columns="columns" :data="tableDataList" :loading="loading">
|
||||
<template #action="data">
|
||||
<ElButton @click="acitonFn(data as TableColumnDefault)">{{
|
||||
t('tableDemo.action')
|
||||
}}</ElButton>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
</template>
|
|
@ -0,0 +1,10 @@
|
|||
declare type TableColumn = {
|
||||
field: string
|
||||
label?: string
|
||||
} & Recordable
|
||||
|
||||
declare type TableColumnDefault = {
|
||||
row: Recordable
|
||||
field: string
|
||||
index: number
|
||||
}
|
Loading…
Reference in New Issue