<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
import { onShow } from '@dcloudio/uni-app';
// 响应式数据
const surveyList = ref([]);
const total = ref(0);
const loading = ref(false);
const formData = reactive({
dcWjTitle: '',
dcId: [],
dcDept: '',
state: null
});
const bdrOptions = ref([]);
const bdrLoading = ref(false);
const pagination = reactive({
current: 1,
size: 10,
total: 0
});
// 被测评人选择器相关
const bdrSearchKeyword = ref('');
const showBdrList = ref(false);
const filteredBdrOptions = ref([]);
const showBdrPlaceholder = ref(true);
// 状态选择器相关
const stateOptions = ref([
{ label: '全部', value: null },
{ label: '已提交', value: 1 },
{ label: '未提交', value: 0 }
]);
const showStateList = ref(false);
// 计算属性
const selectedBdrLabel = computed(() => {
if (formData.dcId.length === 0) return '';
const selected = bdrOptions.value.find(b => b.value === formData.dcId[0]);
return selected ? selected.label : '';
});
const getStateLabel = (value) => {
const state = stateOptions.value.find(s => s.value === value);
return state ? state.label : '全部';
};
// 被测评人选择器方法
const handleBdrFocus = () => {
showBdrList.value = true;
showBdrPlaceholder.value = false;
filterBdrOptions();
};
const handleBdrInput = (e) => {
bdrSearchKeyword.value = e.detail.value;
filterBdrOptions();
};
const handleBdrSelect = (item) => {
formData.dcId = [item.value];
showBdrList.value = false;
bdrSearchKeyword.value = '';
showBdrPlaceholder.value = false;
};
const toggleBdrList = () => {
showBdrList.value = !showBdrList.value;
if (showBdrList.value) {
filterBdrOptions();
}
};
// 状态选择器方法
const handleStateSelect = (state) => {
formData.state = state.value;
showStateList.value = false;
};
const toggleStateList = () => {
showStateList.value = !showStateList.value;
};
// 过滤被测评人选项
const filterBdrOptions = () => {
if (!bdrSearchKeyword.value) {
filteredBdrOptions.value = [...bdrOptions.value];
return;
}
const keyword = bdrSearchKeyword.value.toLowerCase();
filteredBdrOptions.value = bdrOptions.value.filter(item =>
item.label.toLowerCase().includes(keyword)
);
};
// 获取问卷数据
const fetchSurveyData = async () => {
try {
loading.value = true;
const token = uni.getStorageSync('token');
if (!token) {
uni.showToast({ title: '请先登录', icon: 'none' });
uni.redirectTo({ url: '/pages/login/login' });
return;
}
const res = await uni.request({
url: 'http://172.26.26.43/dev-api/wjdc/wj/listTx',
method: 'GET',
data: {
pageNum: pagination.current,
pageSize: pagination.size,
dcWjTitle: formData.dcWjTitle,
dcId: formData.dcId.join(','),
dcDept: formData.dcDept,
state: formData.state
},
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
timeout: 10000 // 增加超时设置
});
// 完整的响应日志
// console.log('API响应:', res);
// 处理不同的响应格式
let responseData = null;
// 处理 uni.request 的不同返回值格式
if (Array.isArray(res)) {
// uni-app 返回数组格式 [error, response]
if (res[0]) throw res[0]; // 如果有错误对象
responseData = res[1].data;
} else {
// 直接返回对象格式
responseData = res.data;
}
// 检查响应状态码
if (responseData?.code === 200) {
surveyList.value = responseData.rows || [];
total.value = responseData.total || 0;
pagination.total = responseData.total || 0;
} else {
// 更详细的错误信息
const errorMsg = responseData?.msg ||
`API返回非200状态: ${responseData?.code || '未知'}`;
throw new Error(errorMsg);
}
} catch (error) {
console.error('请求错误详情:', error);
// 更精确的错误分类
let userFriendlyMsg = '加载问卷数据失败';
if (error.errMsg?.includes('timeout')) {
userFriendlyMsg = '请求超时,请检查网络连接';
}
else if (error.errMsg?.includes('404')) {
userFriendlyMsg = 'API地址不存在,请联系管理员';
}
else if (error.errMsg?.includes('401')) {
userFriendlyMsg = '登录已过期,请重新登录';
uni.redirectTo({ url: '/pages/login/login' });
}
else if (error.message) {
userFriendlyMsg = error.message;
}
uni.showToast({
title: userFriendlyMsg,
icon: 'none',
duration: 3000
});
} finally {
loading.value = false;
}
};
// 清除被测评人选择的方法
const handleClearBdr = (e) => {
e.stopPropagation(); // 阻止事件冒泡
formData.dcId = [];
bdrSearchKeyword.value = '';
showBdrList.value = false;
showBdrPlaceholder.value = true;
};
// 获取被测评人列表
const fetchBdrList = async () => {
try {
const token = uni.getStorageSync('token');
if (!token) {
console.warn('未获取到token,跳过获取被测评人列表');
return;
}
// 打印请求开始信息
// console.log('开始获取被测评人列表...');
const res = await uni.request({
url: 'http://172.26.26.43/dev-api/wjdc/wj/getBdrList',
method: 'GET',
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
timeout: 8000
});
// 详细记录原始响应
// console.log('被测评人API原始响应:', res);
// 统一处理uni-app不同平台的响应格式
let response = null;
if (Array.isArray(res)) {
// 小程序格式 [error, response]
if (res[0]) {
throw res[0];
}
response = res[1];
} else {
// H5格式 { data, statusCode, ... }
response = res;
}
// 检查HTTP状态码
if (response.statusCode !== 200) {
throw new Error(`HTTP错误: ${response.statusCode}`);
}
// 解析响应体
const responseBody = response.data;
// console.log('API响应体:', responseBody);
// 处理不同的API响应结构
let data = [];
if (Array.isArray(responseBody)) {
// 情况1: 直接返回数组
data = responseBody;
} else if (responseBody.data && Array.isArray(responseBody.data)) {
// 情况2: { data: [...] }
data = responseBody.data;
} else if (responseBody.rows && Array.isArray(responseBody.rows)) {
// 情况3: { rows: [...] }
data = responseBody.rows;
} else if (responseBody.result && Array.isArray(responseBody.result)) {
// 情况4: { result: [...] }
data = responseBody.result;
} else {
console.error('无法识别的API响应格式:', responseBody);
throw new Error('无效的API响应格式');
}
// 转换数据格式
bdrOptions.value = data.map(item => {
// 调试输出每个项目
// console.log('被测评人原始项:', item);
// 尝试可能的字段组合
return {
label: item.dcName || item.name || item.username || item.nickname || '未知名称',
value: item.dcId || item.id || item.userId || item.value || null
};
});
// console.log('转换后的被测评人选项:', bdrOptions.value);
// 初始化过滤后的选项
filteredBdrOptions.value = [...bdrOptions.value];
} catch (error) {
console.error('获取被测评人失败:', error);
// 更详细的错误处理
let errorMsg = '获取被测评人失败';
if (error.errMsg) {
if (error.errMsg.includes('401')) {
errorMsg = '认证失败,请重新登录';
uni.redirectTo({ url: '/pages/login/login' });
}
else if (error.errMsg.includes('timeout')) {
errorMsg = '请求超时,请检查网络并重新登录';
uni.redirectTo({ url: '/pages/login/login' });
}
else if (error.errMsg.includes('404')) {
errorMsg = 'API地址已过期,请重新登录';
uni.redirectTo({ url: '/pages/login/login' });
}
else if (error.errMsg.includes('fail')) {
errorMsg = `网络请求失败: ${error.errMsg},请联系管理员`;
}
}
else if (error.message) {
errorMsg = error.message;
}
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 3000
});
} finally {
// 添加加载状态管理
bdrLoading.value = false;
}
};
// 搜索按钮处理
const handleSearch = () => {
pagination.current = 1;
fetchSurveyData();
};
// 重置按钮处理
const handleReset = () => {
formData.dcWjTitle = '';
formData.dcId = [];
formData.dcDept = '';
formData.state = null;
bdrSearchKeyword.value = '';
showBdrList.value = false;
showStateList.value = false;
showBdrPlaceholder.value = true;
handleSearch();
};
// 编辑/查看问卷
const handleView = (item) => {
uni.navigateTo({
url: `/pages/workbench/mycp/operation/operation?id=${item.dcWjId}`
});
};
// 分页大小改变
const handleSizeChange = (e) => {
pagination.size = e.detail.value;
pagination.current = 1;
fetchSurveyData();
};
// 页码改变
const handlePageChange = (page) => {
pagination.current = page;
fetchSurveyData();
};
// 刷新数据
const refreshData = () => {
pagination.current = 1;
fetchSurveyData();
};
// 页面显示时加载数据
onShow(() => {
fetchSurveyData();
fetchBdrList();
});
</script>
<template>
<view class="container">
<!-- 搜索表单区域 -->
<view class="search-card">
<view class="form-group title-group">
<text class="form-label">问卷标题:</text>
<input
v-model="formData.dcWjTitle"
placeholder="请输入问卷标题"
class="form-input title-input"
/>
</view>
<view class="form-row">
<!-- 被测评人选择器 -->
<view class="form-group form-group-half">
<text class="form-label">被测评人:</text>
<view class="search-select-container">
<view class="input-container" @click="showBdrList = true">
<!-- 已选项显示 -->
<view v-if="formData.dcId.length > 0" class="selected-display">
{{ selectedBdrLabel }}
<view class="delete-icon" @click.stop="handleClearBdr">
<text class="x-icon">×</text>
</view>
</view>
<!-- 占位符 -->
<text v-else-if="showBdrPlaceholder" class="placeholder-text">
请选择被测评人
</text>
<!-- 下拉箭头 -->
<view class="dropdown-icon">
<text>▼</text>
</view>
</view>
<!-- 下拉列表 -->
<view v-if="showBdrList" class="dropdown-list">
<view class="search-box">
<input
v-model="bdrSearchKeyword"
placeholder="搜索被测评人"
class="search-input"
@input="filterBdrOptions"
/>
</view>
<scroll-view scroll-y="true" class="dropdown-scroll">
<view
v-for="(item, index) in filteredBdrOptions"
:key="index"
class="dropdown-item"
:class="{ 'selected': formData.dcId[0] === item.value }"
@click="handleBdrSelect(item)"
>
{{ item.label }}
</view>
<view v-if="filteredBdrOptions.length === 0" class="empty-option">
无匹配结果
</view>
</scroll-view>
</view>
<!-- 遮罩层 -->
<view
v-if="showBdrList"
class="dropdown-mask"
@click="showBdrList = false"
></view>
</view>
</view>
<!-- 人员部门 -->
<view class="form-group form-group-half">
<text class="form-label">人员部门:</text>
<input
v-model="formData.dcDept"
placeholder="请输入部门"
class="form-input"
/>
</view>
</view>
<!-- 提交状态和按钮组 -->
<view class="form-row-bottom">
<!-- 提交状态 -->
<view class="state-group">
<text class="form-label">提交状态:</text>
<view class="search-select-container">
<view class="state-select-box" @click="toggleStateList">
<text class="selected-state">
{{ getStateLabel(formData.state) }}
</text>
<view class="dropdown-icon">
<text>▼</text>
</view>
</view>
<!-- 状态下拉列表 -->
<view v-if="showStateList" class="dropdown-list state-dropdown">
<view
v-for="(state, index) in stateOptions"
:key="index"
class="dropdown-item"
:class="{ 'selected': formData.state === state.value }"
@click="handleStateSelect(state)"
>
{{ state.label }}
</view>
</view>
<!-- 遮罩层 -->
<view
v-if="showStateList"
class="dropdown-mask"
@click="showStateList = false"
></view>
</view>
</view>
<!-- 按钮组 -->
<view class="button-group">
<button class="search-button" @click="handleSearch">搜索</button>
<button class="reset-button" @click="handleReset">重置</button>
</view>
</view>
</view>
<!-- 数据显示区域 -->
<view class="data-card">
<view class="card-header">
<button class="refresh-button" @click="refreshData">刷新数据</button>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 数据展示 -->
<view v-else>
<view
v-for="(item, index) in surveyList"
:key="index"
class="data-card-item"
>
<view class="card-header-section">
<view class="card-title">{{ item.dcWjTitle }}</view>
</view>
<view class="card-body-section">
<view class="card-row">
<text class="card-label">被测评人:</text>
<text class="card-value">{{ item.dcName }}</text>
</view>
<view class="card-row">
<text class="card-label">部门:</text>
<text class="card-value">{{ item.dcDept }}</text>
</view>
<view class="card-row">
<text class="card-label">创建时间:</text>
<text class="card-value">{{ item.createTime }}</text>
</view>
<view class="card-row">
<text class="card-label">提交时间:</text>
<text class="card-value">{{ item.updateTime || '-' }}</text>
</view>
</view>
<view class="card-footer-section">
<view class="status-container">
<view
:class="[
'status-tag',
item.state === '1' ? 'status-submitted' : 'status-not-submitted'
]"
>
{{ item.state === '1' ? '已提交' : '未提交' }}
</view>
<view class="score">总分: {{ item.score || '0' }}</view>
</view>
<button class="view-button" @click="handleView(item)">编辑/查看</button>
</view>
</view>
<!-- 空数据提示 -->
<view v-if="surveyList.length === 0" class="empty">
<text>暂无数据</text>
</view>
</view>
<!-- 分页控件 -->
<view v-if="surveyList.length > 0" class="pagination-container">
<picker
mode="selector"
:range="[5, 10, 20, 50]"
:value="[5, 10, 20, 50].indexOf(pagination.size)"
@change="handleSizeChange"
class="page-size-picker"
>
<view class="picker">
每页 {{ pagination.size }} 条
</view>
</picker>
<view class="pagination-buttons">
<button
:disabled="pagination.current === 1"
@click="handlePageChange(pagination.current - 1)"
class="page-button prev-button"
>
上一页
</button>
<text class="page-info">
第 {{ pagination.current }} 页 / 共 {{ Math.ceil(pagination.total / pagination.size) }} 页
</text>
<button
:disabled="pagination.current >= Math.ceil(pagination.total / pagination.size)"
@click="handlePageChange(pagination.current + 1)"
class="page-button next-button"
>
下一页
</button>
</view>
<text class="total-records">共 {{ pagination.total }} 条记录</text>
</view>
</view>
</view>
</template>
在删除选择的被测评人后,下面的下拉列还是显示的之前搜索出来的名字,我想要删除选择的名字后下拉列表和刚开始加载页面一样直接检索所有的名字,如果名字过多就显示前5个旁边其他的隐藏当往上划动的时候显示出隐藏的名字
最新发布