wip: example demo developing

This commit is contained in:
陈凯龙 2022-02-11 17:17:29 +08:00
parent 7ad46f828d
commit ef35bde801
13 changed files with 425 additions and 12 deletions

View File

@ -12,7 +12,7 @@ const count = 100
const baseContent = 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>' '<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: { let List: {
id: string id: string
author: string author: string
title: string title: string
@ -39,7 +39,7 @@ for (let i = 0; i < count; i++) {
} }
export default [ export default [
// 登录接口 // 列表接口
{ {
url: '/example/list', url: '/example/list',
method: 'get', method: 'get',
@ -61,5 +61,36 @@ export default [
} }
} }
} }
},
// 保存接口
{
url: '/example/save',
method: 'post',
timeout,
response: ({ body }) => {
if (!body.id) {
List = [
Object.assign(body, {
id: toAnyString()
})
].concat(List)
return {
code: result_code,
data: 'success'
}
} else {
List.map((item) => {
if (item.id === body.id) {
for (const key in item) {
item[key] = body[key]
}
}
})
return {
code: result_code,
data: 'success'
}
}
}
} }
] as MockMethod[] ] as MockMethod[]

View File

@ -9,3 +9,7 @@ export const getTableListApi = ({ params }: AxiosConfig) => {
list: TableData[] list: TableData[]
}>({ url: '/example/list', method: 'get', params }) }>({ url: '/example/list', method: 'get', params })
} }
export const saveTableApi = ({ data }: AxiosConfig) => {
return request({ url: '/example/save', method: 'post', data })
}

View File

@ -19,6 +19,7 @@ import {
ElDivider ElDivider
} from 'element-plus' } from 'element-plus'
import { InputPassword } from '@/components/InputPassword' import { InputPassword } from '@/components/InputPassword'
import { Editor } from '@/components/Editor'
const componentMap: Recordable<Component, ComponentName> = { const componentMap: Recordable<Component, ComponentName> = {
Radio: ElRadioGroup, Radio: ElRadioGroup,
@ -40,7 +41,8 @@ const componentMap: Recordable<Component, ComponentName> = {
TimeSelect: ElTimeSelect, TimeSelect: ElTimeSelect,
SelectV2: ElSelectV2, SelectV2: ElSelectV2,
RadioButton: ElRadioGroup, RadioButton: ElRadioGroup,
InputPassword: InputPassword InputPassword: InputPassword,
Editor: Editor
} }
export { componentMap } export { componentMap }

View File

@ -57,7 +57,7 @@ export const setGridProp = (col: ColProps = {}): ColProps => {
sm: 12, sm: 12,
md: 12, md: 12,
lg: 12, lg: 12,
xl: 8 xl: 12
}), }),
...col ...col
} }

View File

@ -112,7 +112,7 @@ const setVisible = () => {
<Icon icon="ep:refresh-right" class="mr-5px" /> <Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }} {{ t('common.reset') }}
</ElButton> </ElButton>
<ElButton v-if="showReset" type="text" @click="setVisible"> <ElButton v-if="expand" type="text" @click="setVisible">
{{ t(visible ? 'common.shrink' : 'common.expand') }} {{ t(visible ? 'common.shrink' : 'common.expand') }}
<Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" /> <Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
</ElButton> </ElButton>
@ -130,7 +130,7 @@ const setVisible = () => {
<Icon icon="ep:refresh-right" class="mr-5px" /> <Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }} {{ t('common.reset') }}
</ElButton> </ElButton>
<ElButton v-if="showReset" type="text" @click="setVisible"> <ElButton v-if="expand" type="text" @click="setVisible">
{{ t(visible ? 'common.shrink' : 'common.expand') }} {{ t(visible ? 'common.shrink' : 'common.expand') }}
<Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" /> <Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
</ElButton> </ElButton>

View File

@ -259,13 +259,12 @@ export default defineComponent({
} }
return () => ( return () => (
<> <div v-loading={unref(getProps).loading}>
<ElTable <ElTable
// @ts-ignore // @ts-ignore
ref={elTableRef} ref={elTableRef}
data={unref(getProps).data} data={unref(getProps).data}
{...getBindValue} {...getBindValue}
v-loading={unref(getProps).loading}
> >
{{ {{
default: () => rnderTableColumn(), default: () => rnderTableColumn(),
@ -281,7 +280,7 @@ export default defineComponent({
{...unref(pagination)} {...unref(pagination)}
></ElPagination> ></ElPagination>
) : undefined} ) : undefined}
</> </div>
) )
} }
}) })

View File

@ -22,6 +22,8 @@ interface TableObject<K, L> {
tableList: K[] tableList: K[]
parmasObj: L parmasObj: L
loading: boolean loading: boolean
dialogVisible: boolean
currentRow: Nullable<K>
} }
export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>( export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
@ -39,7 +41,11 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
// AxiosConfig 配置 // AxiosConfig 配置
parmasObj: {} as L, parmasObj: {} as L,
// 加载中 // 加载中
loading: true loading: true,
// 弹窗
dialogVisible: false,
// 当前行的数据
currentRow: null
}) })
const parmasObj = computed(() => { const parmasObj = computed(() => {
@ -93,6 +99,7 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
setProps: (props: Recordable) => void setProps: (props: Recordable) => void
getList: () => Promise<void> getList: () => Promise<void>
setColumn: (columnProps: TableSetPropsType[]) => void setColumn: (columnProps: TableSetPropsType[]) => void
setSearchParmas: (data: Recordable) => void
} = { } = {
getList: async () => { getList: async () => {
tableObject.loading = true tableObject.loading = true
@ -114,6 +121,18 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
setColumn: async (columnProps: TableSetPropsType[]) => { setColumn: async (columnProps: TableSetPropsType[]) => {
const table = await getTable() const table = await getTable()
table?.setColumn(columnProps) table?.setColumn(columnProps)
},
// 与Search组件结合
setSearchParmas: (data: Recordable) => {
tableObject.currentPage = 1
tableObject.parmasObj = Object.assign(tableObject.parmasObj, {
params: {
pageSize: tableObject.pageSize,
pageIndex: tableObject.currentPage,
...data
}
})
methods.getList()
} }
} }

View File

@ -107,7 +107,9 @@ export default {
richText: 'Rich text', richText: 'Rich text',
dialog: 'Dialog', dialog: 'Dialog',
imageViewer: 'Image viewer', imageViewer: 'Image viewer',
descriptions: 'Descriptions' descriptions: 'Descriptions',
example: 'Example',
exampleDialog: 'Example dialog'
}, },
analysis: { analysis: {
newUser: 'New user', newUser: 'New user',
@ -360,5 +362,18 @@ export default {
email: 'Email', email: 'Email',
addr: 'Address', addr: 'Address',
form: 'Combined with Form component' form: 'Combined with Form component'
},
exampleDemo: {
title: 'Title',
add: 'Add',
del: 'Delete',
edit: 'Edit',
author: 'Author',
displayTime: 'Display time',
importance: 'Importance',
pageviews: 'Pageviews',
important: 'Important',
content: 'Content',
save: 'Save'
} }
} }

View File

@ -107,7 +107,9 @@ export default {
richText: '富文本', richText: '富文本',
dialog: '弹窗', dialog: '弹窗',
imageViewer: '图片预览', imageViewer: '图片预览',
descriptions: '描述' descriptions: '描述',
example: '综合示例',
exampleDialog: '综合示例 - 弹窗'
}, },
analysis: { analysis: {
newUser: '新增用户', newUser: '新增用户',
@ -357,5 +359,18 @@ export default {
email: '邮箱', email: '邮箱',
addr: '地址', addr: '地址',
form: '与 Form 组件组合' form: '与 Form 组件组合'
},
exampleDemo: {
title: '标题',
add: '新增',
del: '删除',
edit: '编辑',
author: '作者',
displayTime: '创建时间',
importance: '重要性',
pageviews: '阅读数',
important: '重要',
content: '内容',
save: '保存'
} }
} }

View File

@ -333,6 +333,27 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
} }
} }
] ]
},
{
path: '/example',
component: Layout,
redirect: '/example/example-dialog',
name: 'Example',
meta: {
title: t('router.example'),
icon: 'ep:management',
alwaysShow: true
},
children: [
{
path: 'example-dialog',
component: () => import('@/views/Example/Dialog/ExampleDialog.vue'),
name: 'ExampleDialog',
meta: {
title: t('router.exampleDialog')
}
}
]
} }
] ]

View File

@ -0,0 +1,170 @@
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Search } from '@/components/Search'
import { Dialog } from '@/components/Dialog'
import { useI18n } from '@/hooks/web/useI18n'
import { ElButton, ElTag } from 'element-plus'
import { Table } from '@/components/Table'
import { getTableListApi, saveTableApi } from '@/api/table'
import { useTable } from '@/hooks/web/useTable'
import { TableData } from '@/api/table/types'
import { h, reactive, ref, unref } from 'vue'
import Write from './components/Write.vue'
const { register, tableObject, methods } = useTable<
{
total: number
list: TableData[]
},
TableData
>({
getListApi: getTableListApi,
response: {
list: 'list',
total: 'total'
}
})
const { getList, setSearchParmas } = methods
getList()
const { t } = useI18n()
const searchData: FormSchema[] = [
{
label: t('exampleDemo.title'),
value: '',
component: 'Input',
field: 'title'
}
]
const columns = reactive<TableColumn[]>([
{
field: 'index',
label: t('tableDemo.index'),
type: 'index'
},
{
field: 'content',
label: t('tableDemo.header'),
children: [
{
field: 'title',
label: t('tableDemo.title')
},
{
field: 'author',
label: t('tableDemo.author')
},
{
field: 'display_time',
label: t('tableDemo.displayTime')
},
{
field: 'importance',
label: t('tableDemo.importance'),
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
return h(
ElTag,
{
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
},
() =>
cellValue === 1
? t('tableDemo.important')
: cellValue === 2
? t('tableDemo.good')
: t('tableDemo.commonly')
)
}
},
{
field: 'pageviews',
label: t('tableDemo.pageviews')
}
]
},
{
field: 'action',
label: t('tableDemo.action')
}
])
const AddAction = () => {
tableObject.currentRow = null
tableObject.dialogVisible = true
}
const editAction = (row: TableData) => {
tableObject.currentRow = row
tableObject.dialogVisible = true
}
const writeRef = ref<ComponentRef<typeof Write>>()
const loading = ref(false)
const save = async () => {
loading.value = true
const write = unref(writeRef)
const validate = await write?.elFormRef?.validate()?.catch(() => {})
if (validate) {
const data = await write?.getFormData()
const res = await saveTableApi({
data
})
.catch(() => {})
.finally(() => {
loading.value = false
})
if (res) {
tableObject.dialogVisible = false
tableObject.currentPage = 1
getList()
}
}
}
</script>
<template>
<ContentWrap>
<Search :schema="searchData" @search="setSearchParmas" @reset="setSearchParmas" />
<div class="mb-10px">
<ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
<ElButton type="danger">{{ t('exampleDemo.del') }}</ElButton>
</div>
<Table
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
:columns="columns"
:data="tableObject.tableList"
:loading="tableObject.loading"
:pagination="{
total: tableObject.total
}"
@register="register"
>
<template #action="{ row }">
<ElButton type="primary" @click="editAction(row)">
{{ t('exampleDemo.edit') }}
</ElButton>
</template>
</Table>
</ContentWrap>
<Dialog v-model="tableObject.dialogVisible" :title="t('exampleDemo.add')">
<Write ref="writeRef" :current-row="tableObject.currentRow" />
<template #footer>
<ElButton type="primary" :loading="loading" @click="save">
{{ t('exampleDemo.save') }}
</ElButton>
<ElButton @click="tableObject.dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
</template>
</Dialog>
</template>

View File

@ -0,0 +1,136 @@
<script setup lang="ts">
import { Form } from '@/components/Form'
import { useForm } from '@/hooks/web/useForm'
import { PropType, reactive } from 'vue'
import { TableData } from '@/api/table/types'
import { useI18n } from '@/hooks/web/useI18n'
import { required } from '@/utils/formRules'
import { IDomEditor } from '@wangeditor/editor'
const props = defineProps({
currentRow: {
type: Object as PropType<Nullable<TableData>>,
default: () => null
}
})
const { t } = useI18n()
const schema = reactive<FormSchema[]>([
{
field: 'title',
label: t('exampleDemo.title'),
component: 'Input',
formItemProps: {
rules: [required]
},
colProps: {
span: 24
}
},
{
field: 'author',
label: t('exampleDemo.author'),
component: 'Input',
formItemProps: {
rules: [required]
}
},
{
field: 'display_time',
label: t('exampleDemo.displayTime'),
component: 'DatePicker',
componentProps: {
type: 'datetime',
valueFormat: 'YYYY-MM-DD HH:mm:ss'
},
formItemProps: {
rules: [required]
}
},
{
field: 'importance',
label: t('exampleDemo.importance'),
component: 'Select',
formItemProps: {
rules: [required]
},
componentProps: {
options: [
{
label: '重要',
value: 3
},
{
label: '良好',
value: 2
},
{
label: '一般',
value: 1
}
]
}
},
{
field: 'pageviews',
label: t('exampleDemo.pageviews'),
component: 'InputNumber',
value: 0,
formItemProps: {
rules: [required]
}
},
{
field: 'content',
component: 'Editor',
colProps: {
span: 24
},
componentProps: {
defaultHtml: '',
onChange: (edit: IDomEditor) => {
const { setValues } = methods
setValues({
content: edit.getHtml()
})
}
},
label: t('exampleDemo.content')
}
])
const rules = reactive({
title: [required],
author: [required],
importance: [required],
pageviews: [required],
display_time: [required],
content: [required]
})
const { register, methods, elFormRef } = useForm({
schema
})
if (props.currentRow) {
const { setValues, setSchema } = methods
setValues(props.currentRow)
setSchema([
{
field: 'content',
path: 'componentProps.defaultHtml',
value: props.currentRow.content
}
])
}
defineExpose({
elFormRef,
getFormData: methods.getFormData
})
</script>
<template>
<Form :rules="rules" @register="register" />
</template>

View File

@ -22,6 +22,7 @@ declare global {
| 'TimeSelect' | 'TimeSelect'
| 'SelectV2' | 'SelectV2'
| 'InputPassword' | 'InputPassword'
| 'Editor'
declare type ColProps = { declare type ColProps = {
span?: number span?: number