<!-- 设备资料 equipmentIformation -->
<template>
<view>
<BasicList
:columns="listColumns"
:dispatchUrl="getEquDocument"
:tableDefaultField="tableDefaultField"
:tableDefaultFilter="tableDefaultFilter"
/>
</view>
</template>
<script>
import BasicList from '@/components/BasicList/index.vue';
import {equipInfoColumns} from '@/columns/equipInfoColumns';
import {getEquDocument} from '@/services/base/equClsInfo';
export default{
name: 'EquipmentIformation',
components:{
BasicList
},
props: {
equipmentData: {
type: Object,
required: true,
},
},
setup(props){
const dataList = ref(props.equipmentData);
console.log(dataList.value.equipment_code,'接收到的值');
const listColumns = ref(equipInfoColumns()); 配置列
BasicList 的排序方式
const tableDefaultField = ref({
sort: 'equipment_code',
Order: 'ASC'
});
const tableDefaultFilter = ref({
Key: 'equipment_code', Val: dataList.value.equipment_code, Operator: '=',
});
return{
listColumns,
dataList,
tableDefaultField,
getEquDocument,
tableDefaultFilter
};
}
};
</script>调用 BasicList 组件时报错 VM45229:1 Uncaught (in promise) SyntaxError: "[object Object]" is not valid JSON
at JSON.parse (<anonymous>)
at Proxy.fetch (index.vue:1:1)
at Proxy.beforeMount (index.vue:1:1) 下面是我的 BasicList 组件<!-- src/components/BasicList/index.vue -->
<template>
<scroll-view scroll-y :style="{ height: '100vh' }" @scrolltolower="handleScrollToLower">
<!-- 基础列表组件,当有列数据时才显示 -->
<view v-if="columns.length > 0" class="basic-list">
<!-- 循环渲染每个列表项,使用u-card组件包装 -->
<u-card v-for="(item, index) in newRows" :key="index" :title="''" :border="true" :margin="'20rpx'"
border-radius="0" :padding="20">
<template #body>
<view v-for="(groupCol, groupIndex) in showColumns" :key="groupIndex">
<view v-if="groupCol.showLabel">
<u-row :gutter="24" class="auto-row">
<u-col :span="getColSpan(col,item)" v-for="(col, colIndex) in groupCol.children"
:key="colIndex" class="auto-col">
<view v-if="col.render" class="col-item">
<view class="label-text">{{ col.subTitle }}:</view>
<view class="value-text"><text :style="`color:${getRenderValue(item, col).color};`"
@click="openPreview(getRenderValue(item, col).url)">{{
getRenderValue(item, col).text }}
</text>
</view>
</view>
<view v-else class="col-item">
<view class="label-text">{{ col.subTitle }}:</view>
<view class="value-text"><text style="color: rgba(0, 0, 0, 0.8)">{{ getValue(item, col) }}</text>
</view>
</view>
</u-col>
</u-row>
</view>
<view v-else>
<view class="group-title">{{ groupCol.title }}</view>
<u-row :gutter="10">
<u-col :span="12" v-for="(col, colIndex) in groupCol.children" :key="colIndex">
<view v-if="col.render" class="col-item">
<text class="value-text" v-text="getRenderValue(item, col)"></text>
<view class="label-text">{{ col.subTitle }}</view>
</view>
<view v-else class="col-item">
<text class="value-text" style="color: rgba(0, 0, 0, 0.8)">{{ getValue(item, col) }}</text>
<view class="label-text">{{ col.subTitle }}</view>
</view>
</u-col>
</u-row>
</view>
</view>
</template>
<template #foot>
<slot name="footer" :item="toRaw(item)">
<view class="foot-wrapper">
<u-checkbox v-if="multiple" v-model="item.isChecked" shape="square"
@change="(checked) => handleClickCheckBox(checked, item)">
选择
</u-checkbox>
<view v-else class="button-wrapper">
<u-button @click="handleClick(item)" size="mini" plain shape="circle"
class="custom-button">{{$t('findMore')}}</u-button>
<u-button v-if="item.url" @click="navigateToUrl(item.url)" size="mini" plain
class="custom-button">
跳转链接
</u-button>
</view>
</view>
</slot>
</template>
</u-card>
<!-- 加载更多组件 -->
<view :class="loadmoreClass">
<u-loadmore v-if="moreShow" :status="loadStatus" :loading-text="loadingText" :loadmore-text="loadmoreText"
:nomore-text="nomoreText" :loading-icon="loadingIcon" :color="loadColor" :icon-color="loadColor"
:line-color="lineColor" />
</view>
<view style="height: 48rpx" />
</view>
</scroll-view>
</template>
<script lang="ts">
import { ref, watch } from 'vue';
import { showTS } from '@/utils/utils';
import { useReloadListStore } from '@/state/modules/reload';
import lodash from 'lodash';
import { openInBrowser } from '@/utils/browser';
/**
* BasicList组件
* @description 通用列表展示组件,支持分组展示、多选、分页加载等功能
*/
export default {
name: 'BasicList',
props: {
// 列配置数组,定义列表每一列的展示规则
columns: {
type: Array,
required: true,
},
// 是否开启多选模式
multiple: {
type: Boolean,
required: false,
default: false,
},
// 每页显示的数据条数
pageSize: {
type: Number,
default: 10,
},
// 数据获取方法
dispatchUrl: {
type: Function,
required: true,
},
// 表格默认排序字段
tableDefaultField: {
type: Object,
required: true,
default: function () {
return {
sort: 'Id',
order: 'asc',
};
},
validator: function (val) {
const keys = Object.keys(val);
return keys.includes('sort') && keys.includes('order');
},
},
// 表格默认过滤条件
tableDefaultFilter: {
type: String,
default: '[]',
},
},
setup(props, context) {
const reloadStore = useReloadListStore();
// 数据相关的响应式变量
let rows = ref([]); // 原始数据
let newRows = ref([]); // 处理后的数据
let pageNumber = ref(1); // 当前页码
let moreShow = ref(false); // 是否显示加载更多
let end = ref(false); // 是否已加载全部数据
let showColumns = ref([]); // 处理后的列配置
// 加载更多组件的配置项
const loadStatus = ref('loading')
const loadingText = ref('正在加载...')
const loadmoreText = ref('点击或上拉加载更多')
const nomoreText = ref('没有更多了')
const loadingIcon = ref('circle')
const loadColor = ref('#909399')
const lineColor = ref('#E6E8EB')
const loadmoreClass = ref('loadmore loadmoreHeight');
const handleScrollToLower = () => {
if (!moreShow.value) {
pageNumber.value += 1;
moreShow.value = true;
fetch();
}
}
// 对columns按groupName进行分组处理
const groupColumns = lodash
.chain(props.columns)
.groupBy('groupName')
.map((arr, key) => {
return {
title: key || '其它信息',
showLabel: arr[0] ? arr[0].showLabel || false : false,
groupOrder: arr[0] ? arr[0].groupOrder || 0 : 0,
children: arr,
};
})
.value();
showColumns.value = groupColumns;
console.log(showColumns.value, 'showColumns.value');
// 监听reload事件,重置列表数据
watch(
() => reloadStore.listKey,
(newVal, oldVal) => {
rows.value = [];
newRows.value = [];
pageNumber.value = 1;
end.value = false;
fetch();
},
);
/**
* 获取列表数据
* @description 根据分页参数获取数据,并处理加载状态
*/
const fetch = async () => {
if (end.value) {
loadStatus.value = 'nomore'
moreShow.value = true
return
}
loadStatus.value = 'loading'
moreShow.value = true
const filterArr = [];
const defaultFilter = JSON.parse(props.tableDefaultFilter);
defaultFilter.forEach((defaultFilterItem) => {
filterArr.push(defaultFilterItem);
});
const payload = {
random: new Date().getTime(),
offset: (pageNumber.value - 1) * props.pageSize + 1,
limit: props.pageSize,
filter: JSON.stringify(filterArr),
};
Object.assign(payload, props.tableDefaultField);
const res = await props.dispatchUrl(payload);
if(res.errCode === 0){
loadmoreClass.value = 'loadmore';
}
console.log(res, 'res');
console.log(payload, 'payload');
rows.value = rows.value.concat(res.rows);
newRows.value = rows.value;
console.log(rows.value, 'rows.value');
moreShow.value = false;
if (newRows.value.length > 0 && res.rows.length == 0) {
end.value = true
loadStatus.value = 'nomore'
} else {
loadStatus.value = 'loadmore'
}
if (res.rows.length < props.pageSize) {
end.value = true
loadStatus.value = 'nomore'
}
};
/**
* 下拉刷新处理方法
*/
const onPullDownRefresh = () => {
rows.value = [];
newRows.value = [];
pageNumber.value = 1;
end.value = false;
fetch().then(() => {
console.log('刷新完成');
uni.stopPullDownRefresh();
});
};
/**
* 手动刷新方法
*/
const refresh = () => {
rows.value = [];
newRows.value = [];
pageNumber.value = 1;
end.value = false;
return fetch();
};
/**
* 处理多选框点击事件
* @param checked 是否选中
* @param item 当前项数据
*/
const handleClickCheckBox = (checked, item) => {
Object.assign(item, {
isChecked: checked.value,
});
const filterArr = newRows.value.filter((r) => r.isChecked);
context.emit('onSelectAll', filterArr);
};
let previewUrl = ref('');
const showWebView = ref(false);
const openPreview = (url:any) => {
if (url) {
openInBrowser(url);
} else {
return
}
};
const closePreview = () => {
showWebView.value = false;
};
return {
fetch,
rows,
pageNumber,
newRows,
moreShow,
end,
toRaw,
showColumns,
handleClickCheckBox,
loadStatus,
loadingText,
loadmoreText,
nomoreText,
loadingIcon,
loadColor,
lineColor,
onPullDownRefresh,
refresh,
handleScrollToLower,
previewUrl,
showWebView,
openPreview,
closePreview,
loadmoreClass
};
},
created() {
},
beforeMount() {
// 组件挂载前初始化数据并监听滚动事件
this.rows = [];
this.pageNumber = 1;
this.fetch();
// 监听页面滚动到底部事件
uni.$on('onReachBottom', () => {
console.log('到达底部,onReachBottom');
if (!this.moreShow) {
this.pageNumber += 1;
this.moreShow = true;
this.fetch();
}
});
// 监听下拉刷新事件
uni.$on('onPullDownRefresh', this.onPullDownRefresh);
},
destroyed() {
// 组件销毁时清理数据和事件监听
uni.$off('onReachBottom');
uni.$off('onPullDownRefresh');
this.pageNumber = 1;
this.moreShow = false;
this.end = false;
this.rows = [];
this.newRows = [];
},
methods: {
/**
* 处理点击事件
* @param item 当前点击的项
*/
handleClick(item:any) {
console.log('当前columns配置:', this.columns);
// 获取当前配置类型
// const configType = this.getConfigType();
this.$emit('handleMenuClick', item,);
},
/**
* 处理点击事件
* @param item 当前点击的项
*/
nameClick(item:any) {
this.$emit('handleClick', item);
console.log(item, 'item');
},
/**
* 获取自定义渲染的值
* @param item 当前数据项
* @param col 列配置
* @returns 渲染后的值
*/
getRenderValue(item, col) {
return col.render(item[col.dataIndex], item);
},
/**
* 获取普通值
* @param item 当前数据项
* @param col 列配置
* @returns 格式化后的值
*/
getValue(item, col) {
if (!item) {
return '-';
}
if (!col.dataIndex) {
return '-';
}
if (!item[col.dataIndex]) {
return '-';
}
if (col.valueType === 'dateTs') {
return showTS(Number(item[col.dataIndex]), 'YYYY-MM-DD');
}
return item[col.dataIndex];
},
/**
* 计算列宽度
* @param col 列配置
* @returns 列宽度span值
*/
getColSpan(col,item) {
// const contentLength = (col.subTitle || '').length + 10; // 估算内容长度
// if (contentLength > 20) return 12; // 长内容占满一行
// if (contentLength > 10) return 12; // 中等内容一行放两个
// return 6; // 短内容一行放三个
// 获取标题长度
const titleLength = (col.subTitle || '').length;
// 获取实际渲染值
const value = this.getValue(item, col);
// 计算值长度(确保转为字符串)
const valueLength = String(value).length;
// 总长度 = 标题 + 值 + 1
const totalLength = titleLength + valueLength + 1;
// 动态判断逻辑
if (totalLength > 12) return 12; // 长内容独占一行
return 12; // 短内容(一行最多两列)
},
/**
* 跳转到指定地址
* @param url 跳转的地址
*/
navigateToUrl(url) {
uni.navigateTo({
url: `/pages/webview/index?url=${encodeURIComponent(url)}`,
});
},
},
};
</script>
<style lang="scss" scoped>
.basic-list {
.u-card {
margin: 16rpx; // 缩小外边距
border-radius: 24rpx;
border: 1rpx solid #dcdcdc;
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.15);
overflow: hidden;
}
.group-title {
font-size: 30rpx; // 调整字体大小
font-weight: bold;
color: #1e88e5;
margin-bottom: 12rpx; // 缩小底部间距
border-left: 12rpx solid #1e88e5;
padding-left: 16rpx;
background: linear-gradient(145deg, #f0f8ff, #e6f3ff);
border-radius: 8rpx;
padding: 4rpx 12rpx; // 缩小内边距
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.1);
}
.label-text {
font-size: 26rpx; // 调整字体大小
color: #7f8c8d;
font-weight: 500; // 调整字体粗细
}
.value-text{
font-size: 24rpx;
}
.custom-button {
color: #1e88e5 !important;
padding: 0rpx 20rpx; // 缩小按钮内边距
font-size: 24rpx; // 调整字体大小
}
.col-item {
display: flex;
gap: 12rpx; // 缩小元素间距
padding: 10rpx; // 缩小内边距
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 24rpx; // 调整字体大小
color: #2c3e50;
border-radius: 12rpx;
align-items: center;
}
.u-row {
margin: 0 -8rpx; // 缩小行间距
flex-wrap: wrap;
}
.u-col {
margin-bottom: 16rpx; // 缩小列间距
}
.foot-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12rpx; // 缩小内边距
position: relative ;
z-index: 1;
}
.button-wrapper {
margin-left: auto;
position: relative;
z-index: 1;
}
.auto-row {
display: flex;
flex-wrap: wrap;
margin: 0 -12rpx;
}
.auto-col {
flex: 0 1 auto;
min-width: 220rpx;
max-width: 100%;
padding: 0 12rpx;
box-sizing: border-box;
}
.loadmore{
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.loadmoreHeight{
height: 100vh;
}
}
</style>