Files
yunke/yunkes/src/components/YkStaffDetailModal.vue
T

127 lines
4.4 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { ref, watch } from 'vue'
import type { Page } from '@/api/types'
/**
* 通用明细 modal —— 复刻云客"统计数字点击弹明细表"。
* 调用方传 fetcher(返回 Page<T>)+ 列定义;组件负责 dialog/table/pagination。
*/
interface Col {
prop: string
label: string
width?: number | string
minWidth?: number | string
align?: 'left' | 'center' | 'right'
/** el-table-column formatter (row, column, cellValue, index) → display string. */
formatter?: (row: any, column: any, cellValue: any, index: number) => string
/** 该列单元格可点:渲染为 link button,点击触发回调(用于 modal 内跳聊天等)。 */
onClick?: (row: any) => void
}
const props = defineProps<{
modelValue: boolean
title: string
cols: Col[]
/** fetcher(page, page_size) → Page<T>。宽松 Page<any>:Customer 无 index signature,
* 不可直接赋 Page<Record<string,unknown>>,故用 any 接纳 Page<Customer> 等。 */
fetcher: (page: number, page_size: number) => Promise<Page<any>>
/** 弹框顶部汇总说明(对齐云客) */
summaryText?: string
pageSizes?: number[]
}>()
const emit = defineEmits<{ 'update:modelValue': [boolean] }>()
const loading = ref(false)
const items = ref<any[]>([])
const total = ref(0)
const page = ref(1)
const pageSize = ref(props.pageSizes?.[0] ?? 10)
async function load() {
loading.value = true
try {
const res = await props.fetcher(page.value, pageSize.value)
items.value = res.items
total.value = res.total
} finally {
loading.value = false
}
}
watch(
() => props.modelValue,
(open) => {
if (open) {
page.value = 1
load()
}
},
)
</script>
<template>
<el-dialog
:model-value="modelValue"
:title="title"
width="900px"
@update:model-value="emit('update:modelValue', $event)"
>
<div v-if="summaryText" class="yk-detail-modal__summary">{{ summaryText }}</div>
<el-table v-loading="loading" :data="items" border style="width: 100%" size="small">
<el-table-column
v-for="c in cols"
:key="c.prop"
:prop="c.prop"
:label="c.label"
:width="c.width"
:min-width="c.minWidth"
:align="c.align"
:formatter="c.formatter"
show-overflow-tooltip
>
<template v-if="c.type === 'person'" #default="{ row }">
<div style="display: flex; align-items: center; gap: 8px; padding: 2px 0">
<el-avatar :size="28" :src="row[c.prop + '_avatar'] || undefined" shape="square" style="flex: none; border-radius: 6px;">
{{ (row[c.prop + '_name'] || '?').slice(0, 1) }}
</el-avatar>
<div style="min-width: 0; flex: 1">
<div v-if="c.onClick" >
<el-button link type="primary" size="small" @click="c.onClick?.(row)">
{{ row[c.prop + '_name'] || row[c.prop] || '--' }}
</el-button>
</div>
<div v-else style="font-size: 13px; color: #2b3240; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{{ row[c.prop + '_name'] || row[c.prop] || '--' }}
</div>
<div v-if="row[c.prop + '_sub']" style="font-size: 11px; color: #8a94a4; margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{{ row[c.prop + '_sub'] }}
</div>
<div v-if="row[c.prop + '_real']" style="font-size: 11px; color: #5a6472; margin-top: 1px;">
{{ row[c.prop + '_real'] }}
</div>
</div>
</div>
</template>
<template v-else-if="c.onClick" #default="{ row }">
<el-button link type="primary" size="small" @click="c.onClick?.(row)">
{{ c.formatter ? c.formatter(row, c, row[c.prop], 0) : row[c.prop] }}
</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px; display: flex; justify-content: flex-end">
<el-pagination
:current-page="page"
:page-size="pageSize"
:page-sizes="pageSizes ?? [10, 20, 50]"
:total="total"
layout="total, sizes, prev, pager, next"
background
size="small"
@current-change="(p: number) => { page = p; load() }"
@size-change="(s: number) => { pageSize = s; page = 1; load() }"
/>
</div>
</el-dialog>
</template>