feat: Add example-dialog demo
|
@ -92,5 +92,46 @@ export default [
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 详情接口
|
||||
{
|
||||
url: '/example/detail',
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { id } = query
|
||||
for (const example of List) {
|
||||
if (example.id === id) {
|
||||
return {
|
||||
code: result_code,
|
||||
data: example
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// 删除接口
|
||||
{
|
||||
url: '/example/delete',
|
||||
method: 'post',
|
||||
response: ({ body }) => {
|
||||
const ids = 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: result_code,
|
||||
data: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
] as MockMethod[]
|
||||
|
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
public/logo.png
Before Width: | Height: | Size: 262 KiB After Width: | Height: | Size: 38 KiB |
|
@ -10,6 +10,28 @@ export const getTableListApi = ({ params }: AxiosConfig) => {
|
|||
}>({ url: '/example/list', method: 'get', params })
|
||||
}
|
||||
|
||||
export const saveTableApi = ({ data }: AxiosConfig) => {
|
||||
export const saveTableApi = ({ data }: AxiosConfig<Recordable, TableData>) => {
|
||||
return request({ url: '/example/save', method: 'post', data })
|
||||
}
|
||||
|
||||
export const getTableDetApi = ({
|
||||
params
|
||||
}: AxiosConfig<
|
||||
{
|
||||
id: string
|
||||
},
|
||||
Recordable
|
||||
>) => {
|
||||
return request<TableData>({ url: '/example/detail', method: 'get', params })
|
||||
}
|
||||
|
||||
export const delTableListApi = ({
|
||||
data
|
||||
}: AxiosConfig<
|
||||
Recordable,
|
||||
{
|
||||
id: string[] | number[]
|
||||
}
|
||||
>) => {
|
||||
return request({ url: '/example/delete', method: 'post', data })
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 262 KiB After Width: | Height: | Size: 38 KiB |
|
@ -100,7 +100,7 @@ const toggleClick = () => {
|
|||
</template>
|
||||
|
||||
<template #default>
|
||||
<slot :name="item.field">{{ data[item.field] }}</slot>
|
||||
<slot :name="item.field" :row="data">{{ data[item.field] }}</slot>
|
||||
</template>
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
|
|
|
@ -3,6 +3,7 @@ import Table from './src/Table.vue'
|
|||
export interface TableExpose {
|
||||
setProps: (props: Recordable) => void
|
||||
setColumn: (columnProps: TableSetPropsType[]) => void
|
||||
selections: Recordable[]
|
||||
}
|
||||
|
||||
export { Table }
|
||||
|
|
|
@ -89,9 +89,16 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
const selections = ref<Recordable[]>([])
|
||||
|
||||
const selectionChange = (selection: Recordable[]) => {
|
||||
selections.value = selection
|
||||
}
|
||||
|
||||
expose({
|
||||
setProps,
|
||||
setColumn
|
||||
setColumn,
|
||||
selections
|
||||
})
|
||||
|
||||
const pagination = computed(() => {
|
||||
|
@ -226,7 +233,7 @@ export default defineComponent({
|
|||
align={v.align || align}
|
||||
headerAlign={v.headerAlign || headerAlign}
|
||||
label={v.label}
|
||||
width="100px"
|
||||
width="65px"
|
||||
></ElTableColumn>
|
||||
)
|
||||
} else {
|
||||
|
@ -264,6 +271,7 @@ export default defineComponent({
|
|||
// @ts-ignore
|
||||
ref={elTableRef}
|
||||
data={unref(getProps).data}
|
||||
onSelection-change={selectionChange}
|
||||
{...getBindValue}
|
||||
>
|
||||
{{
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import { Table, TableExpose } from '@/components/Table'
|
||||
import { ElTable } from 'element-plus'
|
||||
import { ElTable, ElMessageBox, ElMessage } from 'element-plus'
|
||||
import { ref, reactive, watch, computed, unref, nextTick } from 'vue'
|
||||
import { AxiosPromise } from 'axios'
|
||||
import { get, assign } from 'lodash-es'
|
||||
import type { TableProps } from '@/components/Table/src/types'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface UseTableConfig<T, L> {
|
||||
getListApi: (option: L) => AxiosPromise<T>
|
||||
delListApi?: (option: AxiosConfig) => AxiosPromise<unknown>
|
||||
// 返回数据格式配置
|
||||
response: {
|
||||
list: string
|
||||
|
@ -22,7 +26,6 @@ interface TableObject<K, L> {
|
|||
tableList: K[]
|
||||
parmasObj: L
|
||||
loading: boolean
|
||||
dialogVisible: boolean
|
||||
currentRow: Nullable<K>
|
||||
}
|
||||
|
||||
|
@ -42,8 +45,6 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
|
|||
parmasObj: {} as L,
|
||||
// 加载中
|
||||
loading: true,
|
||||
// 弹窗
|
||||
dialogVisible: false,
|
||||
// 当前行的数据
|
||||
currentRow: null
|
||||
})
|
||||
|
@ -95,11 +96,31 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
|
|||
return table
|
||||
}
|
||||
|
||||
const delData = async (paramsObj: AxiosConfig, ids: string[] | number[]) => {
|
||||
const res = await (config?.delListApi && config?.delListApi(paramsObj))
|
||||
if (res) {
|
||||
ElMessage.success(t('common.delSuccess'))
|
||||
|
||||
// 计算出临界点
|
||||
const currentPage =
|
||||
tableObject.total % tableObject.pageSize === ids.length || tableObject.pageSize === 1
|
||||
? tableObject.currentPage > 1
|
||||
? tableObject.currentPage - 1
|
||||
: tableObject.currentPage
|
||||
: tableObject.currentPage
|
||||
|
||||
tableObject.currentPage = currentPage
|
||||
methods.getList()
|
||||
}
|
||||
}
|
||||
|
||||
const methods: {
|
||||
setProps: (props: Recordable) => void
|
||||
getList: () => Promise<void>
|
||||
setColumn: (columnProps: TableSetPropsType[]) => void
|
||||
setSearchParmas: (data: Recordable) => void
|
||||
getSelections: () => Promise<K[]>
|
||||
delList: (ids: string[] | number[], multiple: boolean, message?: boolean) => Promise<void>
|
||||
} = {
|
||||
getList: async () => {
|
||||
tableObject.loading = true
|
||||
|
@ -122,6 +143,10 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
|
|||
const table = await getTable()
|
||||
table?.setColumn(columnProps)
|
||||
},
|
||||
getSelections: async () => {
|
||||
const table = await getTable()
|
||||
return (table?.selections || []) as K[]
|
||||
},
|
||||
// 与Search组件结合
|
||||
setSearchParmas: (data: Recordable) => {
|
||||
tableObject.currentPage = 1
|
||||
|
@ -133,6 +158,39 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
|
|||
}
|
||||
})
|
||||
methods.getList()
|
||||
},
|
||||
// 删除数据
|
||||
delList: async (ids: string[] | number[], multiple: boolean, message = true) => {
|
||||
const tableRef = await getTable()
|
||||
if (multiple) {
|
||||
if (!tableRef?.selections.length) {
|
||||
ElMessage.warning(t('common.delNoData'))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (!tableObject.currentRow) {
|
||||
ElMessage.warning(t('common.delNoData'))
|
||||
return
|
||||
}
|
||||
}
|
||||
const paramsObj: AxiosConfig = {
|
||||
data: {
|
||||
ids
|
||||
}
|
||||
}
|
||||
if (message) {
|
||||
ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), {
|
||||
confirmButtonText: t('common.delOk'),
|
||||
cancelButtonText: t('common.delCancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
await delData(paramsObj, ids)
|
||||
})
|
||||
.catch(() => {})
|
||||
} else {
|
||||
await delData(paramsObj, ids)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,13 @@ export default {
|
|||
query: 'Query',
|
||||
reset: 'Reset',
|
||||
shrink: 'Put away',
|
||||
expand: 'Expand'
|
||||
expand: 'Expand',
|
||||
delMessage: 'Delete the selected data?',
|
||||
delWarning: 'Warning',
|
||||
delOk: 'OK',
|
||||
delCancel: 'Cancel',
|
||||
delNoData: 'Please select the data to delete',
|
||||
delSuccess: 'Deleted successfully'
|
||||
},
|
||||
setting: {
|
||||
projectSetting: 'Project setting',
|
||||
|
@ -374,6 +380,7 @@ export default {
|
|||
pageviews: 'Pageviews',
|
||||
important: 'Important',
|
||||
content: 'Content',
|
||||
save: 'Save'
|
||||
save: 'Save',
|
||||
detail: 'Detail'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,13 @@ export default {
|
|||
query: '查询',
|
||||
reset: '重置',
|
||||
shrink: '收起',
|
||||
expand: '展开'
|
||||
expand: '展开',
|
||||
delMessage: '是否删除所选中数据?',
|
||||
delWarning: '提示',
|
||||
delOk: '确定',
|
||||
delCancel: '取消',
|
||||
delNoData: '请选择需要删除的数据',
|
||||
delSuccess: '删除成功'
|
||||
},
|
||||
setting: {
|
||||
projectSetting: '项目配置',
|
||||
|
@ -371,6 +377,7 @@ export default {
|
|||
pageviews: '阅读数',
|
||||
important: '重要',
|
||||
content: '内容',
|
||||
save: '保存'
|
||||
save: '保存',
|
||||
detail: '详情'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { store } from '../index'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { appModules } from '@/config/app'
|
||||
import type { AppState, LayoutType, ThemeTypes } from '@/config/app'
|
||||
import { setCssVar, humpToUnderline } from '@/utils'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const { wsCache } = useCache()
|
||||
|
||||
export const useAppStore = defineStore({
|
||||
id: 'app',
|
||||
state: (): AppState => appModules,
|
||||
|
@ -119,6 +122,7 @@ export const useAppStore = defineStore({
|
|||
return
|
||||
}
|
||||
this.layout = layout
|
||||
wsCache.set('layout', this.layout)
|
||||
},
|
||||
setTitle(title: string) {
|
||||
this.title = title
|
||||
|
@ -132,15 +136,18 @@ export const useAppStore = defineStore({
|
|||
document.documentElement.classList.add('light')
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
wsCache.set('isDark', this.isDark)
|
||||
},
|
||||
setCurrentSize(currentSize: ElememtPlusSzie) {
|
||||
this.currentSize = currentSize
|
||||
wsCache.set('currentSize', this.currentSize)
|
||||
},
|
||||
setMobile(mobile: boolean) {
|
||||
this.mobile = mobile
|
||||
},
|
||||
setTheme(theme: ThemeTypes) {
|
||||
this.theme = Object.assign(this.theme, theme)
|
||||
wsCache.set('theme', this.theme)
|
||||
},
|
||||
setCssVarTheme() {
|
||||
for (const key in this.theme) {
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { store } from '../index'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { localeModules, elLocaleMap } from '@/config/locale'
|
||||
import type { LocaleState } from '@/config/locale'
|
||||
|
||||
const { wsCache } = useCache()
|
||||
|
||||
export const useLocaleStore = defineStore({
|
||||
id: 'locales',
|
||||
state: (): LocaleState => localeModules,
|
||||
|
@ -22,6 +25,7 @@ export const useLocaleStore = defineStore({
|
|||
// this.locale = Object.assign(this.locale, localeMap)
|
||||
this.currentLocale.lang = localeMap?.lang
|
||||
this.currentLocale.elLocale = elLocaleMap[localeMap?.lang]
|
||||
wsCache.set('lang', localeMap?.lang)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -5,11 +5,12 @@ 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 { getTableListApi, saveTableApi, delTableListApi } 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'
|
||||
import Detail from './components/Detail.vue'
|
||||
|
||||
const { register, tableObject, methods } = useTable<
|
||||
{
|
||||
|
@ -19,6 +20,7 @@ const { register, tableObject, methods } = useTable<
|
|||
TableData
|
||||
>({
|
||||
getListApi: getTableListApi,
|
||||
delListApi: delTableListApi,
|
||||
response: {
|
||||
list: 'list',
|
||||
total: 'total'
|
||||
|
@ -82,18 +84,43 @@ const columns = reactive<TableColumn[]>([
|
|||
},
|
||||
{
|
||||
field: 'action',
|
||||
width: '260px',
|
||||
label: t('tableDemo.action')
|
||||
}
|
||||
])
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
const dialogTitle = ref('')
|
||||
|
||||
const AddAction = () => {
|
||||
dialogTitle.value = t('exampleDemo.add')
|
||||
tableObject.currentRow = null
|
||||
tableObject.dialogVisible = true
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const editAction = (row: TableData) => {
|
||||
const delLoading = ref(false)
|
||||
|
||||
const delData = async (row: TableData | null, multiple: boolean) => {
|
||||
tableObject.currentRow = row
|
||||
tableObject.dialogVisible = true
|
||||
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 actionType = ref('')
|
||||
|
||||
const action = (row: TableData, type: string) => {
|
||||
dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
|
||||
actionType.value = type
|
||||
tableObject.currentRow = row
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const writeRef = ref<ComponentRef<typeof Write>>()
|
||||
|
@ -105,7 +132,7 @@ const save = async () => {
|
|||
const validate = await write?.elFormRef?.validate()?.catch(() => {})
|
||||
if (validate) {
|
||||
loading.value = true
|
||||
const data = await write?.getFormData()
|
||||
const data = (await write?.getFormData()) as TableData
|
||||
const res = await saveTableApi({
|
||||
data
|
||||
})
|
||||
|
@ -114,7 +141,7 @@ const save = async () => {
|
|||
loading.value = false
|
||||
})
|
||||
if (res) {
|
||||
tableObject.dialogVisible = false
|
||||
dialogVisible.value = false
|
||||
tableObject.currentPage = 1
|
||||
getList()
|
||||
}
|
||||
|
@ -128,7 +155,9 @@ const save = async () => {
|
|||
|
||||
<div class="mb-10px">
|
||||
<ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
|
||||
<ElButton type="danger">{{ t('exampleDemo.del') }}</ElButton>
|
||||
<ElButton :loading="delLoading" type="danger" @click="delData(null, true)">
|
||||
{{ t('exampleDemo.del') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
|
@ -143,21 +172,29 @@ const save = async () => {
|
|||
@register="register"
|
||||
>
|
||||
<template #action="{ row }">
|
||||
<ElButton type="primary" @click="editAction(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>
|
||||
|
||||
<Dialog v-model="tableObject.dialogVisible" :title="t('exampleDemo.add')">
|
||||
<Write ref="writeRef" :current-row="tableObject.currentRow" />
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<Write v-if="actionType === 'edit'" ref="writeRef" :current-row="tableObject.currentRow" />
|
||||
|
||||
<Detail v-if="actionType === 'detail'" :current-row="tableObject.currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton type="primary" :loading="loading" @click="save">
|
||||
<ElButton v-if="actionType !== 'detail'" type="primary" :loading="loading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="tableObject.dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<script setup lang="ts">
|
||||
import { getTableDetApi } from '@/api/table'
|
||||
import { PropType, watch, ref, 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()
|
||||
|
||||
const props = defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<Nullable<TableData>>,
|
||||
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[]>([
|
||||
{
|
||||
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 v-loading="loading" :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>
|