|
@@ -7,42 +7,57 @@
|
|
|
</view>
|
|
</view>
|
|
|
<view class="table-body">
|
|
<view class="table-body">
|
|
|
<block v-if="data.length">
|
|
<block v-if="data.length">
|
|
|
- <view class="table-row" :class="{ 'has-border': tableConfig.border }" v-for="(row, index) in data" :key="row.id"
|
|
|
|
|
- @click="handleRowClick(row)">
|
|
|
|
|
- <view class="table-row-cell" v-for="item in tableColumns" :key="item.prop" :style="getColumnStyle(item)">
|
|
|
|
|
|
|
+ <view class="table-row" :class="{ 'sibling-border-top': getTableConfig.border }" v-for="(row, index) in data"
|
|
|
|
|
+ :key="getRowKey(row, index)" @click="handleRowClick(row)">
|
|
|
|
|
+ <view class="table-row-cell" v-for="item in tableColumns" :key="item.prop" :style="getCellStyle(item)">
|
|
|
<view v-if="item.type === 'index'">
|
|
<view v-if="item.type === 'index'">
|
|
|
{{ index + 1 }}
|
|
{{ index + 1 }}
|
|
|
</view>
|
|
</view>
|
|
|
<view v-else>
|
|
<view v-else>
|
|
|
- <slot :name="item.slot" :item="row">
|
|
|
|
|
- <text>{{ row[item.prop as keyof StudyRecord] }}</text>
|
|
|
|
|
|
|
+ <slot :name="item.slot" :item="row" :index="index">
|
|
|
|
|
+ <text>{{ getCellValue(row, item.prop) }}</text>
|
|
|
</slot>
|
|
</slot>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</block>
|
|
</block>
|
|
|
- <view v-else class="no-data">暂无数据</view>
|
|
|
|
|
|
|
+ <view v-else class="no-data">{{ getTableConfig.emptyText }}</view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</template>
|
|
</template>
|
|
|
-<script lang="ts" setup>
|
|
|
|
|
|
|
+
|
|
|
|
|
+<script lang="ts" setup generic="T extends Record<string, any>">
|
|
|
import { TableColumnConfig, TableConfig } from '@/types';
|
|
import { TableColumnConfig, TableConfig } from '@/types';
|
|
|
-import { StudyRecord } from '@/types/study';
|
|
|
|
|
|
|
+import { CSSProperties } from 'vue';
|
|
|
|
|
+
|
|
|
|
|
+// 使用泛型定义props
|
|
|
|
|
+interface Props<T> {
|
|
|
|
|
+ tableConfig: TableConfig;
|
|
|
|
|
+ tableColumns: TableColumnConfig[];
|
|
|
|
|
+ data: T[];
|
|
|
|
|
+ cellStyle: CSSProperties;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-const props = defineProps({
|
|
|
|
|
- tableConfig: {
|
|
|
|
|
- type: Object as PropType<TableConfig>,
|
|
|
|
|
- default: () => ({})
|
|
|
|
|
- },
|
|
|
|
|
- tableColumns: {
|
|
|
|
|
- type: Array as PropType<TableColumnConfig[]>,
|
|
|
|
|
- default: () => []
|
|
|
|
|
- },
|
|
|
|
|
- data: {
|
|
|
|
|
- type: Array as PropType<StudyRecord[]>,
|
|
|
|
|
- default: () => []
|
|
|
|
|
- }
|
|
|
|
|
|
|
+const props = defineProps<Props<any>>();
|
|
|
|
|
+
|
|
|
|
|
+// 使用泛型定义emits
|
|
|
|
|
+const emit = defineEmits<{
|
|
|
|
|
+ rowClick: [row: T]
|
|
|
|
|
+}>();
|
|
|
|
|
+
|
|
|
|
|
+const getTableConfig = computed(() => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...{
|
|
|
|
|
+ border: true,
|
|
|
|
|
+ stripe: false,
|
|
|
|
|
+ emptyText: '暂无数据',
|
|
|
|
|
+ loading: false,
|
|
|
|
|
+ rowKey: 'id'
|
|
|
|
|
+ },
|
|
|
|
|
+ ...props.tableConfig
|
|
|
|
|
+ };
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
const getHeaderStyle = (item: TableColumnConfig) => {
|
|
const getHeaderStyle = (item: TableColumnConfig) => {
|
|
|
return {
|
|
return {
|
|
|
flex: item.flex ? item.flex : 1,
|
|
flex: item.flex ? item.flex : 1,
|
|
@@ -50,20 +65,32 @@ const getHeaderStyle = (item: TableColumnConfig) => {
|
|
|
textAlign: item.headerAlign ? item.headerAlign : 'center'
|
|
textAlign: item.headerAlign ? item.headerAlign : 'center'
|
|
|
};
|
|
};
|
|
|
};
|
|
};
|
|
|
-const getColumnStyle = (item: TableColumnConfig) => {
|
|
|
|
|
|
|
+
|
|
|
|
|
+const getCellStyle = (item: TableColumnConfig) => {
|
|
|
return {
|
|
return {
|
|
|
flex: item.flex ? item.flex : 1,
|
|
flex: item.flex ? item.flex : 1,
|
|
|
minWidth: '1px',
|
|
minWidth: '1px',
|
|
|
- textAlign: item.align ? item.align : 'center'
|
|
|
|
|
|
|
+ textAlign: item.align ? item.align : 'center',
|
|
|
|
|
+ ...props.cellStyle
|
|
|
};
|
|
};
|
|
|
};
|
|
};
|
|
|
-const emit = defineEmits<{
|
|
|
|
|
- rowClick: [row: StudyRecord]
|
|
|
|
|
-}>();
|
|
|
|
|
-const handleRowClick = (row: StudyRecord) => {
|
|
|
|
|
|
|
+
|
|
|
|
|
+const handleRowClick = (row: any) => {
|
|
|
emit('rowClick', row);
|
|
emit('rowClick', row);
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+// 安全地获取行key
|
|
|
|
|
+const getRowKey = (row: any, index: number) => {
|
|
|
|
|
+ const rowKey = getTableConfig.value.rowKey;
|
|
|
|
|
+ return row[rowKey] || index;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 安全地获取单元格值
|
|
|
|
|
+const getCellValue = (row: any, prop: string) => {
|
|
|
|
|
+ return row[prop] || '';
|
|
|
|
|
+};
|
|
|
</script>
|
|
</script>
|
|
|
|
|
+
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
|
.table-header {
|
|
.table-header {
|
|
|
@apply flex items-center bg-[#EBF9FF] rounded-5;
|
|
@apply flex items-center bg-[#EBF9FF] rounded-5;
|
|
@@ -77,17 +104,11 @@ const handleRowClick = (row: StudyRecord) => {
|
|
|
@apply flex items-center;
|
|
@apply flex items-center;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.has-border {
|
|
|
|
|
- &+.has-border {
|
|
|
|
|
- @apply border-0 border-solid border-t border-border;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
.table-row-cell {
|
|
.table-row-cell {
|
|
|
@apply px-20 py-20 text-28 text-fore-title;
|
|
@apply px-20 py-20 text-28 text-fore-title;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.no-data {
|
|
.no-data {
|
|
|
- @apply mt-16 bg-[#F6F8FA] text-center py-50 text-26 text-fore-tip-light rounded-5;
|
|
|
|
|
|
|
+ @apply mt-16 bg-[#F6F8FA] text-center py-50 text-26 text-fore-light rounded-5;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|