<template>
<view
class="lh-table"
:style="{
height: processedRows && processedRows.length ? props.height : 'auto',
}"
>
<scroll-view
v-if="processedRows.length"
ref="scrollView"
class="table"
scroll-x
scroll-y
:style="{ height: processedRows.length ? props.height : 'auto' }"
:scroll-into-view="scrollIntoView"
@scrolltolower="scroll"
>
<view class="scrollView">
<view class="table-info">
<!-- 固定列 -->
<view ref="scrollViewContent1" class="fixed-cloumn">
<view class="column" style="display: flex;">
<view
v-if="props.showChecker"
class="column"
style="width: 60rpx"
>
<view
:style="props.headerStyle"
class="column-title"
style="z-index: 10"
>
<checkbox
v-if="!props.radio && props.showCheckerAll"
@tap="checkAll"
:checked="state.checkAll"
:class="state.checkAllClass"
style="width: 34rpx; height: 34rpx"
></checkbox>
</view>
<view
v-for="(row, ind) in processedRows"
:key="ind"
class="column-row column-order"
:class="{
'child-task-row': row.isChild,
'row-even': ind % 2 === 1,
'row-odd': ind % 2 === 0,
}"
>
<checkbox
@tap="changeCheckbox(row, ind)"
:class="{ radio: props.radio }"
:checked="row[props.checkedStr]"
:disabled="Number(row.examineState) === 2"
style="width: 34rpx; height: 34rpx"
></checkbox>
</view>
</view>
<view v-if="props.showIndex" class="column" style="width: 60rpx">
<view :style="props.headerStyle" class="column-title"
>序号</view
>
<view
@tap="tapRow(ind, row)"
v-for="(row, ind) in processedRows"
:key="ind"
class="column-row column-order"
:class="{
'child-task-row': row.isChild,
'row-even': ind % 2 === 1,
'row-odd': ind % 2 === 0,
}"
>
{{ ind + 1 }}
</view>
</view>
<!-- 固定列需指定宽度 -->
<view
v-for="(col, i) in fixedColumnsArray"
:key="i"
class="column fixedBox"
:style="{
width: col.width || '80rpx',
'--column-width': col.width || '80rpx',
}"
>
<view
:style="props.headerStyle"
class="column-title"
@tap="onFixedHeaderChange(col)"
:class="{
'title-left': col.align === 'left',
'title-right': col.align === 'right',
'title-center': col.align === 'center' || !col.align,
}"
>
{{ col.title }}
</view>
<view
v-for="(row, ind) in processedRows"
:key="ind"
class="column-row"
:class="{
'child-task-row': row.isChild,
'row-even': ind % 2 === 1,
'row-odd': ind % 2 === 0,
}"
:style="{
textAlign: col.align || 'center',
justifyContent: getJustifyContent(col.align),
}"
@tap="handleRowTap(row, ind)"
>
<slot :row="row" :name="col.prop" :rowIndex="ind">
<uni-tooltip
v-if="col.tooltip"
:content="row[col.prop]"
placement="bottom"
>
<view
:style="{ width: col.width || 'auto' }"
class="tooltip"
>{{ row[col.prop] }}</view
>
</uni-tooltip>
<view v-else>
{{ row[col.prop] }}
</view>
</slot>
</view>
</view>
</view>
</view>
<!-- 内容列 -->
<view ref="scrollViewContent2" class="arrange-column">
<view
v-for="(col, index) in fixedRowsFalse"
:key="index"
class="column"
:class="{ 'has-child': col && col.hasChild }"
:id="'column' + index"
:scrollIntoView="scrollIntoView"
:style="{ '--column-width': col.width || 'max-content' }"
>
<view>
<view
:style="props.headerStyle"
class="column-title"
@tap="onSortChange(col, index)"
:class="{
'title-left': col.align === 'left',
'title-right': col.align === 'right',
'title-center': col.align === 'center' || !col.align,
}"
>
{{ col.title }}
<view class="arrow-box" v-if="props.isSort">
<text
class="arrow up"
:class="{
active: sortMode === 1 && clickedHeaderIndex === index,
}"
></text>
<text
class="arrow down"
:class="{
active: sortMode === 2 && clickedHeaderIndex === index,
}"
></text>
</view>
</view>
<view v-if="processedRows.length">
<view
v-for="(row, ind) in processedRows"
:key="ind"
class="column-row"
:class="{
'child-task-row': row.isChild,
'row-even': ind % 2 === 1,
'row-odd': ind % 2 === 0,
}"
:style="{
textAlign: col.align || 'center',
whiteSpace: col.width ? 'normal' : 'nowrap',
overflow:(col.slot && col.formate)?'hidden':'none',
justifyContent: getJustifyContent(col.align)
}"
@tap="handleRowTap(row, ind)"
>
<view v-if="col.slot">
<slot :row="row" :name="col.prop" :rowIndex="ind"></slot>
</view>
<view v-else-if="col.formate">
<uni-tooltip
v-if="col.tooltip"
:content="col.formate(row[col.prop], row, ind)"
placement="bottom"
>
<view
:style="{ width: col.width || 'auto' }"
class="tooltip"
>{{ col.formate(row[col.prop], row, ind) }}</view
>
</uni-tooltip>
<text v-else>
{{ col.formate(row[col.prop], row, ind) }}
</text>
</view>
<view v-else>
<uni-tooltip
v-if="col.tooltip"
:content="row[col.prop]"
placement="bottom"
>
<view
:style="{ width: col.width || 'auto' }"
class="tooltip"
>{{ row[col.prop] }}</view
>
</uni-tooltip>
<text v-else>
{{ row[col.prop] }}
</text>
</view>
</view>
</view>
</view>
</view>
<view v-if="props.showOper" class="oper-column">
<view class="column">
<view :style="props.headerStyle" class="column-title"
>操作</view >
<view
v-for="(row, ind) in processedRows"
:key="ind"
class="column-row"
:class="{
'child-task-row': row.isChild,
'row-even': ind % 2 === 1,
'row-odd': ind % 2 === 0,
}"
>
<slot :row="row" :rowIndex="ind" name="operCol"></slot>
</view>
</view>
</view>
</view>
</view>
<uni-load-more
v-if="processedRows.length"
:status="props.loading ? 'loading' : 'noMore'"
:content-text="{
contentdown: '加载中...',
contentrefresh: '加载中...',
contentnomore: '没有更多了',
}"
></uni-load-more>
</view>
</scroll-view>
<uni-load-more
v-else
:status="props.loading ? 'loading' : 'noMore'"
:content-text="{
contentdown: '加载中...',
contentrefresh: '加载中...',
contentnomore: '暂无数据',
}"
></uni-load-more>
<view v-if="props.showFooter" class="footer">
<slot name="footer">
<view>计划放点击翻页</view>
</slot>
</view>
</view>
</template>
<script setup>
import {
ref,
reactive,
getCurrentInstance,
onMounted,
watch,
nextTick,
computed,
} from "vue";
const instance = getCurrentInstance();
const emits = defineEmits([
"tapOper",
"tabRow",
"loadMore",
"changeRadiobox",
"changeCheckbox",
"checkAll",
"refresh",
"cancelCheckbox",
"toggleExpand",
]);
const props = defineProps({
// 判断是否显示序号
showIndex: {
type: Boolean,
default: false,
},
// 用于控制是否显示页脚。默认为false
showFooter: {
type: Boolean,
default: false,
},
// 用于控制是否显示操作按钮
showOper: {
type: Boolean,
default: false,
},
// 用于控制是否显示复选框等
showChecker: {
type: Boolean,
default: false,
},
// 是否展示多选全选按钮
showCheckerAll: {
type: Boolean,
default: true,
},
// 接口判断选中的字段名
checkedStr: {
type: String,
default: "selected",
},
// 默认多选,打开单选
radio: {
type: Boolean,
default: false,
},
// 判断是否显示序号
showIndex: {
type: Boolean,
default: false,
},
// 用于定义表格的列信息。默认为空数组
columns: {
type: Array,
default: () => [],
},
// 用于定义表格的行数据。默认为空数组,初始20条
rows: {
type: Array,
default: () => [],
},
// 用于定义表格头部的样式
headerStyle: {
type: String,
default: "",
},
// 用于定义表格或组件的高度
height: {
type: String,
default: "",
},
// 是否显示加载状态
loading: {
type: Boolean,
default: false,
},
//滚动到的指定位置
scrollIntoView: {
type: String,
default: "column0",
},
// 是否排序
isSort: {
type: Boolean,
default: false,
},
// 是否显示子项
showChildTasks: {
type: Boolean,
default: true,
},
// 子项字段配置
childConfig: {
type: Object,
default: () => ({
childListField: "childList", // 子项列表字段名
childCountField: "childCount", // 子项数量字段名
parentIdField: "id", // 父项ID字段名
}),
},
// 是否启用折叠功能
enableCollapse: {
type: Boolean,
default: true,
},
// 默认折叠状态
defaultCollapsed: {
type: Boolean,
default: false,
},
});
const clickedHeaderIndex = ref(0); //排序下标
const clickedHeaderCode = ref(""); //排序对应的表头code
const sortMode = ref("");
const onSortChange = (column, index) => {
clickedHeaderIndex.value = index;
// 展示下标 上箭头/下箭头
sortMode.value = Number(sortMode.value) + 1;
sortMode.value = sortMode.value < 3 ? sortMode.value : "";
emits("onSortChange", { column, sortMode: sortMode.value, index });
};
// 获取选中的数据(考虑禁用行),用于表头全选样式(含“部分选中”态)
const getCheckList = (array, checkedLabel, key = "id") => {
const enabledRows = (array || []).filter(
(obj) => Number(obj?.examineState) !== 2
);
const checkedEnabled = enabledRows.filter((obj) => obj[checkedLabel]);
if (enabledRows.length === 0) {
state.checkAll = false;
state.checkAllClass = "";
return;
}
if (checkedEnabled.length === enabledRows.length) {
state.checkAll = true;
state.checkAllClass = "";
} else if (checkedEnabled.length === 0) {
state.checkAll = false;
state.checkAllClass = "";
} else {
state.checkAll = false;
state.checkAllClass = "variable";
}
};
// 折叠状态管理
const collapsedRows = ref(new Set());
// 初始化折叠状态
const initCollapseState = () => {
if (props.defaultCollapsed && props.rows) {
props.rows.forEach((row) => {
if (row[props.childConfig.childCountField] > 0) {
collapsedRows.value.add(row[props.childConfig.parentIdField]);
}
});
}
};
// 切换展开/折叠状态
const toggleExpand = (row) => {
if (!props.enableCollapse) return;
const parentId = row[props.childConfig.parentIdField];
if (collapsedRows.value.has(parentId)) {
collapsedRows.value.delete(parentId);
} else {
collapsedRows.value.add(parentId);
}
emits("toggleExpand", {
row,
parentId,
isCollapsed: collapsedRows.value.has(parentId),
});
};
// 折叠相关方法
const expandAll = () => {
collapsedRows.value.clear();
};
const collapseAll = () => {
if (props.rows) {
props.rows.forEach((row) => {
if (row[props.childConfig.childCountField] > 0) {
collapsedRows.value.add(row[props.childConfig.parentIdField]);
}
});
}
};
const expandRow = (parentId) => {
collapsedRows.value.delete(parentId);
};
const collapseRow = (parentId) => {
collapsedRows.value.add(parentId);
};
const getCollapsedRows = () => {
return Array.from(collapsedRows.value);
};
defineExpose({
getCheckList,
onSortChange,
expandAll,
collapseAll,
expandRow,
collapseRow,
getCollapsedRows,
toggleExpand,
}); //抛出才去得到
const arrangeColumnListWidth = ref(0);
const arrangeColumnWidth = ref(0);
const extractNumberFromString = (input) => {
// 使用正则表达式匹配数字部分
const match = input.match(/\d+/);
// 如果匹配成功,则返回匹配到的数字,否则返回null
return match ? parseInt(match[0], 10) : null;
};
const state = reactive({
checkAll: false,
checkAllClass: "",
height: `calc(${props.height} ? '60px' : '1px') - ${
props.showFooter ? "80px" : "1px"
}`,
});
const isCheckboxDisabled = (row) => false;
// 纵向滚动条事件
const scroll = (e) => {
// console.log('开始gun')
if (e.detail.direction === "bottom") {
emits("loadMore");
console.log("滑动到底部");
return;
}
};
// 点击单元格事件
const tapRow = (ind, row) => {
emits("tapRow", {
index: ind,
data: row,
});
};
// 处理行点击事件
const handleRowTap = (row, ind) => {
// 如果启用了折叠功能,且当前行有子项且不是子项本身,则切换折叠状态
if (props.enableCollapse && !row.isChild && row.hasChild) {
toggleExpand(row);
}
// 无论是否折叠,都触发普通的行点击事件
tapRow(ind, row);
};
const onFixedHeaderChange = (column) => {
emits("onFixedHeaderChange", { column });
};
// 根据对齐方式获取justify-content值
const getJustifyContent = (align) => {
switch (align) {
case "left":
return "flex-start";
case "right":
return "flex-end";
case "center":
default:
return "center";
}
};
// 全选(考虑禁用行)
const checkAll = () => {
state.checkAllClass = "";
// 如果当前是半选(variable),点击后应切换为全选
if (state.checkAllClass === "variable") {
state.checkAll = true;
} else {
state.checkAll = !state.checkAll;
}
props.rows.forEach((row) => {
if (Number(row.examineState) !== 2) {
row[props.checkedStr] = state.checkAll;
}
});
emits("checkAll", { data: processedRows.value, selected: state.checkAll });
};
// 选中
const changeCheckbox = (row, ind) => {
if (Number(row.examineState) === 2) return;
row[props.checkedStr] = !row[props.checkedStr];
// 单选
if (props.radio) {
props.rows.forEach((v, i) => {
v[props.checkedStr] = ind === i;
});
emits("changeRadiobox", { index: ind, data: row });
} else {
// 多选
const allCountLength = processedRows.value.length;
const checkedCount = processedRows.value.filter(
(row) => row[props.checkedStr]
);
const checkedCountLength = checkedCount.length;
if (allCountLength == checkedCountLength) {
state.checkAllClass = "";
state.checkAll = true;
}
if (!checkedCountLength) {
state.checkAllClass = "";
state.checkAll = false;
}
if (checkedCountLength && checkedCountLength < allCountLength) {
state.checkAll = false;
state.checkAllClass = "variable";
}
if (!row[props.checkedStr]) {
//取消选中
emits("cancelCheckbox", row);
}
emits("changeCheckbox", checkedCount);
}
};
// 除固定列内容
const fixedRowsFalse = computed(() => {
return props.columns.filter((col) => !col.fixed);
});
const fixedColumnsArray = computed(() => {
return props.columns.filter((col) => col.fixed);
});
// 处理子项数据的计算属性
const processedRows = computed(() => {
// 每次变化都更新状态
getCheckList(props.rows, props.checkedStr);
if (!props.showChildTasks || !Array.isArray(props.rows)) {
return props.rows || [];
}
const result = [];
const { childListField, childCountField, parentIdField } = props.childConfig;
props.rows.forEach((item) => {
// 添加父任务
result.push({
...item,
hasChild: (item[childCountField] || 0) > 0,
isChild: false,
_raw: item,
});
// 如果有子任务且显示子任务,且父项未折叠,添加子任务
if (
item[childListField] &&
Array.isArray(item[childListField]) &&
item[childListField].length > 0 &&
!collapsedRows.value.has(item[parentIdField])
) {
item[childListField].forEach((child) => {
result.push({
...child,
hasChild: (child[childCountField] || 0) > 0,
isChild: true,
parentId: item[parentIdField],
_raw: child,
});
});
}
});
return result;
});
// 监听数据变化,重置折叠状态
watch(
() => props.rows,
(newRows) => {
if (newRows && props.defaultCollapsed) {
initCollapseState();
}
},
{ deep: true }
);
// 初始化折叠状态
onMounted(() => {
initCollapseState();
});
</script>
<style>
</style>
<style lang="scss" scoped>
::v-deep .uni-scroll-view-content {
display: inline-flex;
}
.lh-table {
flex: 1;
width: 100%;
overflow: hidden;
}
.fixed-cloumn {
position: sticky;
left: -3rpx;
z-index: 10;
display: inline-block;
background: #fff;
border-left: 2rpx solid #fff;
border-right: 2rpx solid #fff;
.fixedBox {
background: #fff;
z-index: 99;
width: var(--column-width, auto);
box-sizing: border-box;
}
.column-title {
z-index: 99;
background: linear-gradient(#ececfc, #ffffff) !important;
border-top: 2rpx solid #ffffff;
border-bottom: none !important;
width: 100%;
box-sizing: border-box;
}
// 移除固定列内部的 nth-child 斑马纹,统一用 row-even/row-odd 控制
// 固定列子项样式
.column-row.child-task-row {
background-color: #f8f9fa !important;
border-left: 8rpx solid #e9ecef;
padding-left: 20rpx;
font-size: 22rpx;
color: #6c757d;
min-height: 80rpx;
position: relative;
&::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 8rpx solid #dee2e6;
border-top: 6rpx solid transparent;
border-bottom: 6rpx solid transparent;
}
&:hover {
background-color: #e9ecef !important;
border-left-color: #adb5bd;
}
}
}
.oper-column {
position: sticky;
right: 0;
z-index: 10;
display: inline-block;
background: #fff;
.column {
border: 2rpx solid #fff !important;
border-top: none !important;
}
.column-title {
z-index: 99;
background: linear-gradient(#ececfc, #ffffff) !important;
border-top: 2rpx solid #ffffff;
border-bottom: none !important;
}
// 移除操作列内部的 nth-child 斑马纹,统一用 row-even/row-odd 控制
// 操作列子项样式
.column-row.child-task-row {
background-color: #f8f9fa !important;
border-left: 8rpx solid #e9ecef;
padding-left: 20rpx;
font-size: 22rpx;
color: #6c757d;
min-height: 80rpx;
position: relative;
&::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 8rpx solid #dee2e6;
border-top: 6rpx solid transparent;
border-bottom: 6rpx solid transparent;
}
&:hover {
background-color: #e9ecef !important;
border-left-color: #adb5bd;
}
}
}
.footer {
height: 80px;
padding: 16rpx;
font-size: 18px;
}
.table {
width: 100%;
box-sizing: border-box;
white-space: nowrap;
display: flex;
padding: 13rpx;
.table-info {
display: flex;
flex: 1;
}
.arrange-column {
flex: 1;
display: inline-flex;
border-right: 2rpx solid #fff;
.arrange-column-box {
display: flex;
flex: 1;
// overflow-x: scroll;
}
}
.column {
display: inline-block;
// border-left: 1px solid #eee;
flex-grow: 1;
width: max-content;
background: #fff;
// 移除列内 nth-child 斑马纹,避免与全局行级类冲突
}
.column:first-child {
border-left: none;
}
.column.has-child {
flex: none;
flex-shrink: 0;
}
.oper:nth-child(n + 2) {
margin-left: 16rpx;
}
}
.column-row {
text-align: center;
padding: 10rpx;
box-sizing: border-box;
text-overflow: ellipsis;
min-width: 60rpx;
font-size: 24rpx;
color: #000000;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
min-height: 94rpx;
background-color: #fff;
border-bottom: 2rpx solid rgba(123, 123, 123, 0.2);
// overflow: hidden;
width: 100%;
word-break: break-all;
white-space: nowrap;
position: relative;
&.disabled-row {
opacity: 0.6;
}
// 子项样式优化
&.child-task-row {
background-color: #f8f9fa !important;
border-left: 8rpx solid #e9ecef;
padding-left: 20rpx;
font-size: 22rpx;
color: #6c757d;
min-height: 80rpx;
position: relative;
// 子项左侧缩进指示器
&::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 8rpx solid #dee2e6;
border-top: 6rpx solid transparent;
border-bottom: 6rpx solid transparent;
}
// 子项悬停效果
&:hover {
background-color: #e9ecef !important;
border-left-color: #adb5bd;
}
// 子项内容样式
.child-content {
display: flex;
align-items: center;
width: 100%;
// 子项图标
.child-icon {
width: 24rpx;
height: 24rpx;
margin-right: 8rpx;
color: #6c757d;
}
// 子项文本
.child-text {
flex: 1;
text-align: left;
}
}
}
}
.column-title {
height: 64rpx !important;
line-height: 40rpx;
font-weight: 500;
color: #868686;
position: sticky;
position: -webkit-sticky;
top: -2rpx !important;
font-size: 24rpx;
padding: 10rpx;
z-index: 9;
background: linear-gradient(#ececfc, #ffffff);
border-top: 2rpx solid #ffffff;
border-bottom: none !important;
text-align: center;
overflow: hidden;
width: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
word-break: break-all;
white-space: nowrap;
&.title-left {
justify-content: flex-start;
text-align: left;
}
&.title-right {
justify-content: flex-end;
text-align: right;
}
&.title-center {
justify-content: center;
text-align: center;
}
}
::v-deep .uni-checkbox-input {
width: 32rpx;
height: 32rpx;
box-sizing: border-box;
border: 1px solid #7b7b7b;
background-color: transparent;
}
::v-deep .uni-checkbox-input svg {
color: #e70c0c;
path {
fill: #e70c0c !important;
}
}
.radio {
::v-deep .uni-checkbox-input {
border-radius: 50%;
}
}
::v-deep .variable .uni-checkbox-input::after {
height: 30rpx;
content: "-";
font-size: 28px;
line-height: 20rpx;
color: #e70c0c;
display: inline-block;
}
.no_more {
text-align: center;
font-size: 24rpx;
font-weight: 500;
color: #868686;
padding: 0 40rpx;
}
.loading-state {
text-align: center;
padding: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.loading-spinner {
width: 40rpx;
height: 40rpx;
border: 4rpx solid #f3f3f3;
border-top: 4rpx solid #e70c0c;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 16rpx;
}
text {
font-size: 24rpx;
color: #868686;
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.state-more {
padding: 10rpx;
flex-direction: row;
font-size: 24rpx;
color: #868686;
text-align: center;
.loading-spinner {
width: 25rpx;
height: 25rpx;
margin-bottom: 0rpx;
margin-right: 16rpx;
}
}
// 排序图标
.arrow-box {
position: relative;
bottom: 0rpx;
right: 8rpx;
top: -5rpx;
display: inline-block;
}
.arrow {
display: block;
position: relative;
width: 10px;
height: 8px;
// border: 1px red solid;
left: 5px;
overflow: hidden;
cursor: pointer;
}
.down {
top: 3px;
}
.down::after {
content: "";
width: 8px;
height: 8px;
position: absolute;
left: 2px;
top: -5px;
transform: rotate(45deg);
background-color: #ccc;
}
.down.active::after {
background-color: #e70c0c;
}
.up::after {
content: "";
width: 8px;
height: 8px;
position: absolute;
left: 2px;
top: 5px;
transform: rotate(45deg);
background-color: #ccc;
}
.up.active::after {
background-color: #e70c0c;
}
.scrollView {
width: 100%;
display: inline-table;
box-sizing: border-box;
}
// 展示省略号
.tooltip {
white-space: nowrap; /* 禁止换行 */
overflow: hidden; /* 隐藏溢出内容 */
text-overflow: ellipsis;
}
// 子项层级样式增强
.child-task-row {
// 子项缩进层级样式
&.level-1 {
padding-left: 40rpx;
border-left-width: 8rpx;
}
&.level-2 {
padding-left: 60rpx;
border-left-width: 12rpx;
}
&.level-3 {
padding-left: 80rpx;
border-left-width: 16rpx;
}
// 子项状态样式
&.status-active {
border-left-color: #28a745;
background-color: #f8fff9 !important;
}
&.status-pending {
border-left-color: #ffc107;
background-color: #fffdf8 !important;
}
&.status-completed {
border-left-color: #6c757d;
background-color: #f8f9fa !important;
}
&.status-error {
border-left-color: #dc3545;
background-color: #fff8f8 !important;
}
}
.row-even {
background-color: rgba(190, 190, 190, 0.2) !important;
}
.row-odd {
background-color: #fff !important;
}
</style>
uni app 表格封装
于 2025-07-23 12:33:58 首次发布
3522

被折叠的 条评论
为什么被折叠?



