Browse Source

计划bug处理

dev-v1.0.2
庄锐波 4 months ago
parent
commit
0ddf2cf39c
  1. 468
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue

468
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_system.vue

@ -25,64 +25,76 @@
v-for="item in reserveList"
:key="item.id"
class="reserve-card"
:class="{ 'approved-card': item.opStatus === 1, 'rejected-card': item.opStatus === 2, 'pending-card': item.opStatus === 3, 'selected-card': selectedId === item.id }"
@click="onSelectCard(item)"
:class="{ 'approved-card': item.opStatus === 1, 'rejected-card': item.opStatus === 2, 'pending-card': item.opStatus === 3 }"
>
<div class="card-header">
<div class="card-label">系统匹配</div>
<div class="status" :class="getStatusClass(item.opStatus)">
{{ getStatusText(item.opStatus) }}
</div>
</div>
<div class="plan-title">{{ item.enforceContent || '执法事项' }}</div>
<div class="info-row">
<div class="label">企业名称:</div>
<div class="value">{{ item.enterpriseName || '-' }}</div>
</div>
<div class="info-row">
<div class="label">计划时间:</div>
<div class="value">{{ formatDateShort(item.planDate) }} {{ getPlanTimeText(item.planTime) }}</div>
</div>
<div class="info-row">
<div class="label">执法单位:</div>
<div class="value">{{ item.bureauDeptName || '-' }}</div>
</div>
<div class="info-row">
<div class="label">匹配时间:</div>
<div class="value">{{ formatDate(item.reserveTime) }}</div>
<!-- 本单位信息 -->
<div class="unit-block">
<div class="unit-block-header">
<span class="unit-block-tag">本单位计划</span>
</div>
<div class="unit-block-title">{{ item.enterpriseName || '-' }}</div>
<div class="unit-block-subtitle">{{ formatDateShort(item.planDate) }} {{ getPlanTimeText(item.planTime) }}</div>
<div class="unit-block-divider"></div>
<div class="info-row">
<div class="label">执法单位:</div>
<div class="value">{{ item.bureauDeptName || '-' }}</div>
</div>
<div class="info-row">
<div class="label">执法人员:</div>
<div class="value pre-line">{{ formatUserInfo(item.enforceUserNames, item.enforceUserTels) }}</div>
</div>
<div class="info-row">
<div class="label">执法事项:</div>
<div class="value">{{ item.enforceContent || '-' }}</div>
</div>
<div class="info-row">
<div class="label">匹配时间:</div>
<div class="value">{{ formatDate(item.reserveTime) }}</div>
</div>
</div>
<!-- 选中时显示关联计划信息 -->
<div v-if="selectedId === item.id && groupMembers.length > 0" class="group-members">
<div class="group-title">关联计划</div>
<div v-for="(member, idx) in groupMembers" :key="member.id" class="member-item">
<div class="member-row">
<span class="member-label">预约状态:</span>
<span class="member-value member-status" :class="getStatusClass(member.opStatus)">{{ getStatusText(member.opStatus) }}</span>
<!-- 关联计划(默认展开) -->
<div v-if="groupMembersMap[item.id] && groupMembersMap[item.id].length > 0" class="unit-block unit-block-target">
<div class="unit-block-header">
<span class="unit-block-tag unit-block-tag-target">关联单位 ({{ groupMembersMap[item.id].length }})</span>
</div>
<div v-for="(member, idx) in groupMembersMap[item.id]" :key="member.id" class="member-section">
<div class="unit-block-title">{{ member.bureauDeptName || '-' }}</div>
<div class="unit-block-divider"></div>
<div class="info-row">
<div class="label">计划时间:</div>
<div class="value">{{ formatDateShort(member.planDate) }} {{ getPlanTimeText(member.planTime) }}</div>
</div>
<div class="member-row">
<span class="member-label">执法单位:</span>
<span class="member-value">{{ member.bureauDeptName || '-' }}</span>
<div class="info-row">
<div class="label">预约状态:</div>
<div class="value">
<span :class="'status-text ' + getStatusClass(member.opStatus)">{{ getStatusText(member.opStatus) }}</span>
</div>
</div>
<div class="member-row">
<span class="member-label">执法事项:</span>
<span class="member-value">{{ member.enforceContent || '-' }}</span>
<div class="info-row">
<div class="label">执法人员:</div>
<div class="value pre-line">{{ formatUserInfo(member.enforceUserNames, member.enforceUserTels) }}</div>
</div>
<div class="member-row">
<span class="member-label">计划时间:</span>
<span class="member-value">{{ formatDateShort(member.planDate) }} {{ getPlanTimeText(member.planTime) }}</span>
<div class="info-row">
<div class="label">执法事项:</div>
<div class="value">{{ member.enforceContent || '-' }}</div>
</div>
<div v-if="idx < groupMembers.length - 1" class="member-divider"></div>
<div v-if="idx < groupMembersMap[item.id].length - 1" class="member-divider"></div>
</div>
</div>
<div v-if="selectedId === item.id && groupLoading" class="group-loading">加载中...</div>
<div v-else-if="item.planGroupId && groupLoadingSet.has(item.id)" class="unit-block unit-block-target">
<div class="group-loading">加载关联计划中...</div>
</div>
<!-- 选中且待确认状态才显示操作按钮 -->
<div v-if="item.opStatus === 3 && selectedId === item.id" class="card-actions">
<!-- 待确认状态直接显示操作按钮 -->
<div v-if="item.opStatus === 3" class="card-actions">
<div class="actions-left">
<div v-if="item.opMessage" class="action-item message-action" @click.stop="onShowMessage(item)">
<van-icon name="chat-o" />
@ -138,9 +150,8 @@
import { showConfirmDialog, showSuccessToast, showDialog } from 'vant';
import { getReserveList, getReserveDetail, approveReserve, rejectReserveBySystem, getReserveGroupMembers } from '@/api/enforcement/reserve';
const selectedId = ref(null);
const groupMembers = ref([]); // 选中卡片的同组关联计划
const groupLoading = ref(false);
const groupMembersMap = ref({}); // key=item.id, value=关联计划列表
const groupLoadingSet = ref(new Set());
const reserveList = ref([]);
const loading = ref(false);
const finished = ref(false);
@ -181,6 +192,17 @@ const getPlanTimeText = (time) => {
return map[time] || '';
};
// 格式化执法人员信息(姓名+电话多行展示)
const formatUserInfo = (names, tels) => {
if (!names) return '-';
const nameArr = names.split(',');
const telArr = tels ? tels.split(',') : [];
return nameArr.map((n, i) => {
const tel = telArr[i] ? ' ' + telArr[i].trim() : '';
return n.trim() + tel;
}).join('\n');
};
// 格式化日期(带时分)
const formatDate = (dateStr) => {
if (!dateStr) return '-';
@ -210,37 +232,13 @@ const onLoad = () => {
fetchList();
};
// 选中卡片(再次点击取消选中)
const onSelectCard = (item) => {
if (selectedId.value === item.id) {
selectedId.value = null;
groupMembers.value = [];
return;
}
selectedId.value = item.id;
groupMembers.value = [];
// 加载同组关联计划
if (item.planGroupId) {
groupLoading.value = true;
getReserveGroupMembers(
item.planGroupId,
item.id,
(res) => {
groupLoading.value = false;
if (res.code === 200) {
groupMembers.value = res.data || [];
}
},
() => { groupLoading.value = false; }
);
}
};
// 下拉刷新
const onRefresh = () => {
pageNum.value = 1;
finished.value = false;
reserveList.value = [];
groupMembersMap.value = {};
groupLoadingSet.value = new Set();
fetchList();
};
@ -274,6 +272,8 @@ const fetchList = () => {
} else {
pageNum.value++;
}
// 第二次请求:自动加载关联计划
loadGroupMembers(rows);
} else {
finished.value = true;
}
@ -286,6 +286,28 @@ const fetchList = () => {
);
};
// 自动加载关联计划(第二次请求)
const loadGroupMembers = (rows) => {
const itemsWithGroup = rows.filter(item => item.planGroupId && !groupMembersMap.value[item.id]);
if (itemsWithGroup.length === 0) return;
itemsWithGroup.forEach(item => {
groupLoadingSet.value.add(item.id);
getReserveGroupMembers(
item.planGroupId,
item.id,
(res) => {
groupLoadingSet.value.delete(item.id);
if (res.code === 200) {
groupMembersMap.value[item.id] = res.data || [];
}
},
() => {
groupLoadingSet.value.delete(item.id);
}
);
});
};
// 查看备注
const onShowMessage = (item) => {
showDialog({
@ -348,7 +370,8 @@ const buildTimeOptions = (item) => {
});
}
// 关联计划的时间
for (const member of groupMembers.value) {
const members = groupMembersMap.value[item.id] || [];
for (const member of members) {
if (member.planDate) {
options.push({
bureauDeptName: member.bureauDeptName || '-',
@ -374,7 +397,7 @@ const doApprove = (id, params) => {
approveReserve(id, params, (res) => {
if (res.code === 200) {
showSuccessToast('已同意');
afterOperate();
onRefresh();
} else {
showDialog({ title: '提示', message: res.msg || '操作失败' });
}
@ -390,21 +413,13 @@ const onReject = (item) => {
rejectReserveBySystem(item.id, (res) => {
if (res.code === 200) {
showSuccessToast('已拒绝');
afterOperate();
onRefresh();
} else {
showDialog({ title: '提示', message: res.msg || '操作失败' });
}
});
}).catch(() => {});
};
// 操作后置处理
const afterOperate = () => {
selectedId.value = null;
groupMembers.value = [];
pendingApproveItem.value = null;
onRefresh();
};
</script>
<style lang="less" scoped>
@ -446,70 +461,64 @@ const afterOperate = () => {
.reserve-card {
background: #fff;
border-radius: 14px;
padding: 16px;
padding: 0;
box-shadow: 0 4px 16px rgba(44, 116, 190, 0.12), 0 1px 4px rgba(0, 0, 0, 0.06);
border: 2.5px solid #1a5fb4;
overflow: hidden;
}
.approved-card {
border: 1.5px solid rgba(7, 193, 96, 0.4);
}
.rejected-card {
border: 1.5px solid rgba(238, 10, 36, 0.35);
}
.pending-card {
border: 1.5px solid rgba(35, 130, 207, 0.35);
}
.selected-card {
background: radial-gradient(circle at 78% 5%, rgba(65, 188, 255, 0.75) 0, rgba(65, 188, 255, 0.25) 20%, transparent 42%),
linear-gradient(135deg, #064ec6 0%, #0b63d9 48%, #268ee5 100%) !important;
border-color: transparent;
.card-header .status {
color: #f0b66c !important;
}
.plan-title {
color: #fff;
}
.info-row .label {
color: #fff;
}
> .card-header {
padding: 12px 16px;
background: linear-gradient(135deg, #0d4fad 0%, #1565c8 50%, #1e7fe0 100%);
.info-row .value {
color: rgba(255, 255, 255, 0.7);
.card-label {
background: rgba(255, 255, 255, 0.18);
color: rgba(255, 255, 255, 0.9);
}
&.msg {
color: rgba(255, 255, 255, 0.8);
.status {
color: rgba(255, 255, 255, 0.85) !important;
}
}
.card-actions {
border-top-color: rgba(255, 255, 255, 0.18);
> .reserve-time-row {
margin: 0 16px;
padding: 10px 0;
}
.message-action {
color: rgba(255, 255, 255, 0.85);
> .card-actions {
margin: 0 16px;
padding-bottom: 14px;
padding-top: 12px;
border-top: 1px solid #999;
}
}
.approve-action {
color: #fff;
}
.approved-card {
border-color: #07c160;
}
.reject-action {
color: rgba(255, 255, 255, 0.8);
}
.rejected-card {
border-color: #ee0a24;
}
.pending-card {
border-color: #1a5fb4;
}
.card-header {
display: flex;
justify-content: flex-end;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
margin-bottom: 0;
}
.card-label {
font-size: 13px;
font-weight: 500;
color: #1f75c9;
background: #e8f3ff;
padding: 3px 10px;
border-radius: 10px;
}
.status {
@ -522,15 +531,12 @@ const afterOperate = () => {
&.gray { color: #999; }
}
.plan-title {
font-size: 16px;
font-weight: 700;
color: #111;
line-height: 1.5;
margin-bottom: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.status-text {
font-weight: 500;
&.blue { color: #1f75c9; }
&.green { color: #07c160; }
&.red { color: #ee0a24; }
&.gray { color: #999; }
}
.info-row {
@ -552,12 +558,144 @@ const afterOperate = () => {
flex: 1;
color: #8a8a8a;
&.msg {
color: #666;
font-style: italic;
&.pre-line {
white-space: pre-line;
}
}
.unit-block {
background: linear-gradient(135deg, #0d4fad 0%, #1565c8 50%, #1e7fe0 100%);
padding: 14px 16px;
margin-bottom: 0;
border-bottom: 1px solid rgba(255,255,255,0.12);
.unit-block-tag {
background: rgba(255, 255, 255, 0.18);
color: rgba(255, 255, 255, 0.9);
}
.unit-block-title {
color: #fff;
}
.unit-block-subtitle {
color: rgba(255, 255, 255, 0.7);
}
.unit-block-divider {
border-bottom-color: rgba(255, 255, 255, 0.2);
}
.label {
color: rgba(255, 255, 255, 0.85);
}
.value {
color: rgba(255, 255, 255, 0.75);
}
}
.unit-block-target {
padding: 14px 16px;
border-bottom: none;
background: #fff;
.unit-block-tag {
background: #fff7e6;
color: #fa8c16;
}
.unit-block-title {
color: #111;
}
.unit-block-subtitle {
color: #999;
}
.unit-block-divider {
border-bottom-color: #eef2f7;
}
.label {
color: #333;
}
.value {
color: #8a8a8a;
}
}
.unit-block-header {
margin-bottom: 6px;
}
.unit-block-tag {
display: inline-block;
font-size: 12px;
font-weight: 500;
color: #1f75c9;
background: #e8f3ff;
padding: 2px 8px;
border-radius: 8px;
}
.unit-block-tag-target {
color: #fa8c16;
background: #fff7e6;
}
.unit-block-title {
font-size: 15px;
font-weight: 700;
color: #111;
line-height: 1.5;
margin-bottom: 4px;
}
.unit-block-subtitle {
font-size: 13px;
color: #999;
margin-bottom: 10px;
}
.unit-block-divider {
border-bottom: 1px solid #eef2f7;
margin-bottom: 10px;
}
.member-section {
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px dashed #999;
&:last-child {
padding-bottom: 0;
margin-bottom: 0;
border-bottom: none;
}
}
.member-divider {
height: 1px;
background: #eef2f7;
margin: 4px 0 10px;
}
.group-loading {
font-size: 13px;
color: #999;
text-align: center;
padding: 10px 0;
}
.reserve-time-row {
margin-top: 0;
padding-top: 10px;
padding-bottom: 4px;
border-top: 1px solid #eef2f7;
}
.card-actions {
margin-top: 14px;
padding-top: 12px;
@ -599,56 +737,6 @@ const afterOperate = () => {
color: #ee0a24;
}
.group-members {
margin-top: 12px;
padding-top: 10px;
border-top: 1px dashed rgba(255, 255, 255, 0.2);
}
.group-title {
font-size: 13px;
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 8px;
}
.member-item {
padding: 6px 0;
}
.member-row {
display: flex;
align-items: flex-start;
margin-bottom: 4px;
font-size: 13px;
line-height: 1.4;
}
.member-label {
width: 80px;
flex-shrink: 0;
white-space: nowrap;
color: rgba(255, 255, 255, 0.75);
}
.member-value {
flex: 1;
color: rgba(255, 255, 255, 0.6);
}
.member-divider {
height: 1px;
background: rgba(255, 255, 255, 0.1);
margin: 6px 0;
}
.group-loading {
margin-top: 10px;
font-size: 12px;
color: rgba(255, 255, 255, 0.6);
text-align: center;
}
.time-picker-content {
padding: 16px;
}

Loading…
Cancel
Save