feat: Add example-page demo

This commit is contained in:
kailong321200875 2022-02-19 14:26:05 +08:00
parent 262f4211cf
commit 1492f9119a
14 changed files with 639 additions and 36 deletions

View File

@ -37,6 +37,7 @@
"element-plus": "2.0.2", "element-plus": "2.0.2",
"intro.js": "^5.0.0", "intro.js": "^5.0.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mitt": "^3.0.0",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.11", "pinia": "^2.0.11",

View File

@ -38,6 +38,7 @@ specifiers:
less: ^4.1.2 less: ^4.1.2
lint-staged: ^12.3.4 lint-staged: ^12.3.4
lodash-es: ^4.17.21 lodash-es: ^4.17.21
mitt: ^3.0.0
mockjs: ^1.1.0 mockjs: ^1.1.0
nprogress: ^0.2.0 nprogress: ^0.2.0
pinia: ^2.0.11 pinia: ^2.0.11
@ -86,6 +87,7 @@ dependencies:
element-plus: registry.npmmirror.com/element-plus/2.0.2_1a412d14def5ff5ca1122000e4bee666 element-plus: registry.npmmirror.com/element-plus/2.0.2_1a412d14def5ff5ca1122000e4bee666
intro.js: registry.npmmirror.com/intro.js/5.0.0 intro.js: registry.npmmirror.com/intro.js/5.0.0
lodash-es: registry.nlark.com/lodash-es/4.17.21 lodash-es: registry.nlark.com/lodash-es/4.17.21
mitt: registry.npmmirror.com/mitt/3.0.0
mockjs: registry.npmmirror.com/mockjs/1.1.0 mockjs: registry.npmmirror.com/mockjs/1.1.0
nprogress: registry.npmmirror.com/nprogress/0.2.0 nprogress: registry.npmmirror.com/nprogress/0.2.0
pinia: registry.npmmirror.com/pinia/2.0.11_typescript@4.5.5+vue@3.2.31 pinia: registry.npmmirror.com/pinia/2.0.11_typescript@4.5.5+vue@3.2.31
@ -10127,6 +10129,17 @@ packages:
version: 1.2.5 version: 1.2.5
dev: true dev: true
registry.npmmirror.com/mitt/3.0.0:
resolution:
{
integrity: sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==,
registry: https://registry.npm.taobao.org/,
tarball: https://registry.npmmirror.com/mitt/-/mitt-3.0.0.tgz
}
name: mitt
version: 3.0.0
dev: false
registry.npmmirror.com/mixin-deep/1.3.2: registry.npmmirror.com/mixin-deep/1.3.2:
resolution: resolution:
{ {

View File

@ -27,7 +27,7 @@ export const filterMenusPath = (
for (const v of routes) { for (const v of routes) {
let data: Nullable<AppRouteRecordRaw> = null let data: Nullable<AppRouteRecordRaw> = null
const meta = (v.meta ?? {}) as RouteMeta const meta = (v.meta ?? {}) as RouteMeta
if (!meta.hidden) { if (!meta.hidden || meta.showMainRoute) {
const allParentPaht = getAllParentPath<AppRouteRecordRaw>(allRoutes, v.path) const allParentPaht = getAllParentPath<AppRouteRecordRaw>(allRoutes, v.path)
const fullPath = isUrl(v.path) ? v.path : allParentPaht.join('/') const fullPath = isUrl(v.path) ? v.path : allParentPaht.join('/')

23
src/hooks/web/useEmitt.ts Normal file
View File

@ -0,0 +1,23 @@
import mitt from 'mitt'
import { onBeforeUnmount } from 'vue'
interface Option {
name: string // 事件名称
callback: Fn // 回调
}
const emitter = mitt()
export const useEmitt = (option?: Option) => {
if (option) {
emitter.on(option.name, option.callback)
onBeforeUnmount(() => {
emitter.off(option.name)
})
}
return {
emitter
}
}

View File

@ -115,7 +115,11 @@ export default {
imageViewer: 'Image viewer', imageViewer: 'Image viewer',
descriptions: 'Descriptions', descriptions: 'Descriptions',
example: 'Example', example: 'Example',
exampleDialog: 'Example dialog' exampleDialog: 'Example dialog',
examplePage: 'Example page',
exampleAdd: 'Example page - add',
exampleEdit: 'Example page - edit',
exampleDetail: 'Example page - detail'
}, },
analysis: { analysis: {
newUser: 'New user', newUser: 'New user',

View File

@ -115,7 +115,11 @@ export default {
imageViewer: '图片预览', imageViewer: '图片预览',
descriptions: '描述', descriptions: '描述',
example: '综合示例', example: '综合示例',
exampleDialog: '综合示例 - 弹窗' exampleDialog: '综合示例 - 弹窗',
examplePage: '综合示例 - 页面',
exampleAdd: '综合示例 - 新增',
exampleEdit: '综合示例 - 编辑',
exampleDetail: '综合示例 - 详情'
}, },
analysis: { analysis: {
newUser: '新增用户', newUser: '新增用户',

View File

@ -352,6 +352,53 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
meta: { meta: {
title: t('router.exampleDialog') title: t('router.exampleDialog')
} }
},
{
path: 'example-page',
component: () => import('@/views/Example/Page/ExamplePage.vue'),
name: 'ExamplePage',
meta: {
title: t('router.examplePage')
}
},
{
path: 'example-add',
component: () => import('@/views/Example/Page/ExampleAdd.vue'),
name: 'ExampleAdd',
meta: {
title: t('router.exampleAdd'),
noTagsView: true,
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page'
}
},
{
path: 'example-edit',
component: () => import('@/views/Example/Page/ExampleEdit.vue'),
name: 'ExampleEdit',
meta: {
title: t('router.exampleEdit'),
noTagsView: true,
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page'
}
},
{
path: 'example-detail',
component: () => import('@/views/Example/Page/ExampleDetail.vue'),
name: 'ExampleDetail',
meta: {
title: t('router.exampleDetail'),
noTagsView: true,
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page'
}
} }
] ]
} }

View File

@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { getTableDetApi } from '@/api/table' import { PropType, reactive } from 'vue'
import { PropType, watch, ref, reactive } from 'vue'
import type { TableData } from '@/api/table/types' import type { TableData } from '@/api/table/types'
import { Descriptions } from '@/components/Descriptions' import { Descriptions } from '@/components/Descriptions'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -8,42 +7,13 @@ import { ElTag } from 'element-plus'
const { t } = useI18n() const { t } = useI18n()
const props = defineProps({ defineProps({
currentRow: { currentRow: {
type: Object as PropType<Nullable<TableData>>, type: Object as PropType<Nullable<TableData>>,
default: () => null default: () => null
} }
}) })
const currentRow = ref<Nullable<TableData>>(null)
const loading = ref(false)
const getTableDet = async () => {
loading.value = true
const res = await getTableDetApi({
params: {
id: props.currentRow?.id as string
}
}).finally(() => {
loading.value = false
})
if (res) {
currentRow.value = res.data
}
}
watch(
() => props.currentRow,
() => {
getTableDet()
},
{
deep: true,
immediate: true
}
)
const schema = reactive<DescriptionsSchema[]>([ const schema = reactive<DescriptionsSchema[]>([
{ {
field: 'title', field: 'title',
@ -75,7 +45,7 @@ const schema = reactive<DescriptionsSchema[]>([
</script> </script>
<template> <template>
<Descriptions v-loading="loading" :schema="schema" :data="currentRow || {}"> <Descriptions :schema="schema" :data="currentRow || {}">
<template #importance="{ row }: { row: TableData }"> <template #importance="{ row }: { row: TableData }">
<ElTag :type="row.importance === 1 ? 'success' : row.importance === 2 ? 'warning' : 'danger'"> <ElTag :type="row.importance === 1 ? 'success' : row.importance === 2 ? 'warning' : 'danger'">
{{ {{

View File

@ -0,0 +1,54 @@
<script setup lang="ts">
import Write from './components/Write.vue'
import { ContentWrap } from '@/components/ContentWrap'
import { ref, unref } from 'vue'
import { ElButton } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n'
import { useRouter } from 'vue-router'
import { saveTableApi } from '@/api/table'
import { TableData } from '@/api/table/types'
import { useEmitt } from '@/hooks/web/useEmitt'
const { emitter } = useEmitt()
const { push } = useRouter()
const { t } = useI18n()
const writeRef = ref<ComponentRef<typeof Write>>()
const loading = ref(false)
const save = async () => {
const write = unref(writeRef)
const validate = await write?.elFormRef?.validate()?.catch(() => {})
if (validate) {
loading.value = true
const data = (await write?.getFormData()) as TableData
const res = await saveTableApi({
data
})
.catch(() => {})
.finally(() => {
loading.value = false
})
if (res) {
emitter.emit('getList', 'add')
push('/example/example-page')
}
}
}
</script>
<template>
<ContentWrap :title="t('exampleDemo.add')">
<Write ref="writeRef" />
<div class="text-center">
<ElButton type="primary" :loading="loading" @click="save">
{{ t('exampleDemo.save') }}
</ElButton>
<ElButton @click="push('/example/example-page')">{{ t('dialogDemo.close') }}</ElButton>
</div>
</ContentWrap>
</template>

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import Detail from './components/Detail.vue'
import { ContentWrap } from '@/components/ContentWrap'
import { ref } from 'vue'
import { ElButton } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n'
import { useRouter, useRoute } from 'vue-router'
import { getTableDetApi } from '@/api/table'
import { TableData } from '@/api/table/types'
const { push } = useRouter()
const { query } = useRoute()
const { t } = useI18n()
const currentRow = ref<Nullable<TableData>>(null)
const getTableDet = async () => {
const res = await getTableDetApi({
params: {
id: query.id as string
}
})
if (res) {
currentRow.value = res.data
}
}
getTableDet()
</script>
<template>
<ContentWrap :title="t('exampleDemo.detail')">
<Detail :current-row="currentRow" />
<div class="text-center">
<ElButton @click="push('/example/example-page')">{{ t('dialogDemo.close') }}</ElButton>
</div>
</ContentWrap>
</template>

View File

@ -0,0 +1,71 @@
<script setup lang="ts">
import Write from './components/Write.vue'
import { ContentWrap } from '@/components/ContentWrap'
import { ref, unref } from 'vue'
import { ElButton } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n'
import { useRouter, useRoute } from 'vue-router'
import { saveTableApi, getTableDetApi } from '@/api/table'
import { TableData } from '@/api/table/types'
import { useEmitt } from '@/hooks/web/useEmitt'
const { emitter } = useEmitt()
const { push } = useRouter()
const { query } = useRoute()
const { t } = useI18n()
const currentRow = ref<Nullable<TableData>>(null)
const getTableDet = async () => {
const res = await getTableDetApi({
params: {
id: query.id as string
}
})
if (res) {
currentRow.value = res.data
}
}
getTableDet()
const writeRef = ref<ComponentRef<typeof Write>>()
const loading = ref(false)
const save = async () => {
const write = unref(writeRef)
const validate = await write?.elFormRef?.validate()?.catch(() => {})
if (validate) {
loading.value = true
const data = (await write?.getFormData()) as TableData
const res = await saveTableApi({
data
})
.catch(() => {})
.finally(() => {
loading.value = false
})
if (res) {
emitter.emit('getList', 'edit')
push('/example/example-page')
}
}
}
</script>
<template>
<ContentWrap :title="t('exampleDemo.edit')">
<Write ref="writeRef" :current-row="currentRow" />
<div class="text-center">
<ElButton type="primary" :loading="loading" @click="save">
{{ t('exampleDemo.save') }}
</ElButton>
<ElButton @click="push('/example/example-page')">{{ t('dialogDemo.close') }}</ElButton>
</div>
</ContentWrap>
</template>

View File

@ -0,0 +1,166 @@
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { Search } from '@/components/Search'
import { useI18n } from '@/hooks/web/useI18n'
import { ElButton, ElTag } from 'element-plus'
import { Table } from '@/components/Table'
import { getTableListApi, delTableListApi } from '@/api/table'
import { useTable } from '@/hooks/web/useTable'
import { TableData } from '@/api/table/types'
import { h, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useEmitt } from '@/hooks/web/useEmitt'
defineOptions({
name: 'ExamplePage'
})
const { push } = useRouter()
const { register, tableObject, methods } = useTable<
{
total: number
list: TableData[]
},
TableData
>({
getListApi: getTableListApi,
delListApi: delTableListApi,
response: {
list: 'list',
total: 'total'
}
})
const { getList, setSearchParmas } = methods
getList()
useEmitt({
name: 'getList',
callback: (type: string) => {
if (type === 'add') {
tableObject.currentPage = 1
}
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: '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',
width: '260px',
label: t('tableDemo.action')
}
])
const AddAction = () => {
push('/example/example-add')
}
const delLoading = ref(false)
const delData = async (row: TableData | null, multiple: boolean) => {
tableObject.currentRow = row
const { delList, getSelections } = methods
const selections = await getSelections()
delLoading.value = true
await delList(
multiple ? selections.map((v) => v.id) : [tableObject.currentRow?.id as string],
multiple
).finally(() => {
delLoading.value = false
})
}
const action = (row: TableData, type: string) => {
push(`/example/example-${type}?id=${row.id}`)
}
</script>
<template>
<ContentWrap>
<Search :schema="searchData" @search="setSearchParmas" @reset="setSearchParmas" />
<div class="mb-10px">
<ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
<ElButton :loading="delLoading" type="danger" @click="delData(null, true)">
{{ t('exampleDemo.del') }}
</ElButton>
</div>
<Table
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
:columns="columns"
:data="tableObject.tableList"
:loading="tableObject.loading"
:pagination="{
total: tableObject.total
}"
@register="register"
>
<template #action="{ row }">
<ElButton type="primary" @click="action(row, 'edit')">
{{ t('exampleDemo.edit') }}
</ElButton>
<ElButton type="success" @click="action(row, 'detail')">
{{ t('exampleDemo.detail') }}
</ElButton>
<ElButton type="danger" @click="delData(row, false)">
{{ t('exampleDemo.del') }}
</ElButton>
</template>
</Table>
</ContentWrap>
</template>

View File

@ -0,0 +1,65 @@
<script setup lang="ts">
import { PropType, reactive } from 'vue'
import type { TableData } from '@/api/table/types'
import { Descriptions } from '@/components/Descriptions'
import { useI18n } from '@/hooks/web/useI18n'
import { ElTag } from 'element-plus'
const { t } = useI18n()
defineProps({
currentRow: {
type: Object as PropType<Nullable<TableData>>,
default: () => null
}
})
const schema = reactive<DescriptionsSchema[]>([
{
field: 'title',
label: t('exampleDemo.title'),
span: 24
},
{
field: 'author',
label: t('exampleDemo.author')
},
{
field: 'display_time',
label: t('exampleDemo.displayTime')
},
{
field: 'importance',
label: t('exampleDemo.importance')
},
{
field: 'pageviews',
label: t('exampleDemo.pageviews')
},
{
field: 'content',
label: t('exampleDemo.content'),
span: 24
}
])
</script>
<template>
<Descriptions :schema="schema" :data="currentRow || {}">
<template #importance="{ row }: { row: TableData }">
<ElTag :type="row.importance === 1 ? 'success' : row.importance === 2 ? 'warning' : 'danger'">
{{
row.importance === 1
? t('tableDemo.important')
: row.importance === 2
? t('tableDemo.good')
: t('tableDemo.commonly')
}}
</ElTag>
</template>
<template #content="{ row }: { row: TableData }">
<div v-html="row.content"></div>
</template>
</Descriptions>
</template>

View File

@ -0,0 +1,144 @@
<script setup lang="ts">
import { Form } from '@/components/Form'
import { useForm } from '@/hooks/web/useForm'
import { PropType, reactive, watch } from 'vue'
import { TableData } from '@/api/table/types'
import { useI18n } from '@/hooks/web/useI18n'
import { 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
})
watch(
() => props.currentRow,
(currentRow) => {
if (!currentRow) return
const { setValues, setSchema } = methods
setValues(currentRow)
setSchema([
{
field: 'content',
path: 'componentProps.defaultHtml',
value: currentRow.content
}
])
},
{
deep: true,
immediate: true
}
)
defineExpose({
elFormRef,
getFormData: methods.getFormData
})
</script>
<template>
<Form :rules="rules" @register="register" />
</template>