Browse Source

页面bug修改

dev
wangmingyong@163.com 1 month ago
parent
commit
16a24bdd84
  1. 2
      zdxt-web-client/zdxt-efcode-enforcement-app/src/api/nos/common.js
  2. 18
      zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/nos/RegisterPopups.vue
  3. 4
      zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/register/RegisterPopups.vue
  4. 58
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enforce/enforcementChat.vue
  5. 4
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enforce/enforcementRegister.vue
  6. 2
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/message/enforcementReserveList.vue
  7. 2
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/message/enforcementTaskList.vue
  8. 312
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/notice/cp_notice_message copy.vue
  9. 509
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/notice/cp_notice_message.vue
  10. 8
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/statistics/statisticsAnalysis.vue

2
zdxt-web-client/zdxt-efcode-enforcement-app/src/api/nos/common.js

@ -7,5 +7,7 @@ const baseUrl = "/app-enf";
export const queryListAndQrInfo = (params, success, error) => _post(baseUrl + "/enterpriseInfo/queryListAndQrInfo", params, success, error);
// 执法端-分页查询用户
export const queryUsersByPage = (params, success, error) => _get(baseUrl + "/system/queryUsersByPage", params, success, error);
// 查询执法单位下的用户列表
export const getBureauUnitUsers = (params, success, error) => _get("/enforcementcode/bureauUnit/users", params, success, error);
// 查询执依据告列表
export const queryAccordingList = (params, success, error) => _get(baseUrl + "/according/list", params, success, error);

18
zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/nos/RegisterPopups.vue

@ -86,7 +86,9 @@
<script setup name="RegisterPopups">
import { ref, reactive, watch, computed } from "vue"
import { queryListAndQrInfo, queryUsersByPage, queryAccordingList } from "@/api/nos/common"
import { queryListAndQrInfo, getBureauUnitUsers, queryAccordingList } from "@/api/nos/common"
const { useCommon } = $globalStore
const props = defineProps({
visible: {
@ -281,20 +283,22 @@ const loadPersonList = () => {
if (personFinished.value) return
personLoading.value = true
queryUsersByPage(
getBureauUnitUsers(
{
pageSize: personPageSize.value,
sysDeptId: useCommon.bureauUnitId || "",
keyword: personSearchKeyword.value || "",
globalSearch: "false",
pageNum: personPageNum.value,
lawEnforcerName: personSearchKeyword.value || undefined,
pageSize: personPageSize.value,
},
(res) => {
console.log(res)
if (res.code === 200) {
const data = res.rows.map(item => ({
if (res.code === 200 || res.code === 0) {
const data = (res.data || res.rows || []).map(item => ({
userId: item.userId,
userName: item.userName,
phonenumber: item.phonenumber,
lawEnforcerIdCard: item.lawCertificateNum,
lawEnforcerIdCard: item.mobileNumber,
lawEnforcerId: item.userId,
lawEnforcerName: item.userName,
lawEnforcerPhone: item.phonenumber,

4
zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/register/RegisterPopups.vue

@ -58,7 +58,7 @@
<div v-for="(person, index) in personList" :key="index" class="person-list-item"
@click="handleSelectPerson(person)">
<div class="person-name">{{ person.userName }}</div>
<div class="person-id">执法证件号{{ person.phonenumber }}</div>
<div class="person-id">执法证件号{{ person.mobileNumber }}</div>
</div>
<van-empty v-if="personList.length === 0 && !personLoading" description="暂无数据" />
</van-list>
@ -283,7 +283,7 @@ const handleSelectPerson = (person) => {
// userName namephonenumber id
emit('selectPerson', {
name: person.userName || person.name || '',
id: person.phonenumber || person.id || ''
id: person.mobileNumber || person.id || ''
});
localPersonPopup.value = false;
};

58
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enforce/enforcementChat.vue

@ -1,17 +1,5 @@
<template>
<div class="chat-page">
<!-- 顶部导航栏 -->
<!-- <van-nav-bar
title="在线联系"
left-text="关闭"
left-arrow
@click-left="onClickLeft"
>
<template #right>
<van-icon name="ellipsis" size="20" @click="onMoreClick" />
</template>
</van-nav-bar> -->
<!-- 聊天对象信息 -->
<!-- <div class="chat-header">
<div class="chat-target">{{ bureauDeptName }} · {{ contactPerson }}</div>
@ -37,13 +25,14 @@
class="message-row"
:class="{ 'self': msg.sender === 'self' }"
>
<div v-if="msg.msgFrom === 1" class="msg-avatar left-avatar"></div>
<!-- 对方头像在左 -->
<img
<!-- <img
v-if="msg.sender === 'other'"
:src="msg.avatar"
class="avatar"
alt="avatar"
/>
/> -->
<div class="message-content">
<div
@ -61,17 +50,19 @@
@touchend="onTouchEnd"
@touchmove="onTouchMove"
>
{{ msg.content }}
<div class="msg-sender">{{ msg.msgFrom === 0 ? (msg.bureauDeptName || useCommon.bureauUnitName || '执法单位') : msg.userName }}</div>
<span>{{ msg.content }}</span>
</div>
</div>
<div v-if="msg.msgFrom === 0" class="msg-avatar right-avatar"></div>
<!-- 自己头像在右 -->
<img
<!-- <img
v-if="msg.sender === 'self'"
:src="msg.avatar"
class="avatar"
alt="avatar"
/>
/> -->
</div>
</div>
</div>
@ -136,10 +127,6 @@ const noticeId = route.query.noticeId|| '';
const bureauDeptName = ref(route.query.bureauDeptName || '深圳司法局');
const contactPerson = ref(route.query.contactPerson || '付明明 周昂');
const noticeTitle = ref(route.query.title || '安全生产检查');
// ========== ==========
const selfAvatar = 'https://placehold.co/40x40/447bf5/fff?text=我';
const otherAvatar = 'https://placehold.co/40x40/999/fff?text=企';
// ========== ==========
const messageList = ref([]);
// ========== ==========
@ -150,11 +137,12 @@ const loadMessageList = () => {
const lastItem = res.data[res.data.length - 1];
messageList.value = res.data.map(item => ({
id: item.id,
sender: item.userName == useCommon.userName ? 'self' : 'other',
sender: item.msgFrom === 0 ? 'self' : 'other',
content: item.content || item.message || '',
time: item.createTime || item.time || '',
avatar: item.userName == useCommon.userName ? selfAvatar : otherAvatar,
isRecalled: item.isRecalled || false,
msgFrom: item.msgFrom || 0,
userName: item.userName || '',
}));
nextTick(() => scrollToBottom());
if (lastItem && lastItem.id) {
@ -444,7 +432,6 @@ const onMoreClick = () => {
display: flex;
align-items: flex-start;
margin-bottom: 0.2rem;
&.self {
justify-content: flex-end;
}
@ -491,6 +478,7 @@ const onMoreClick = () => {
text-align: center;
border-radius: 0.08rem;
}
.msg-sender { color: #ccc; }
}
/* ===== 长按/右击菜单 ===== */
@ -609,4 +597,24 @@ const onMoreClick = () => {
opacity: 0.8;
}
}
.msg-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: 600;
color: #fff;
flex-shrink: 0;
}
.left-avatar {
background: linear-gradient(135deg, #f5a623, #f7c948);
}
.right-avatar {
background: linear-gradient(135deg, #1890ff, #0b63d1);
}
</style>

4
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enforce/enforcementRegister.vue

@ -504,7 +504,7 @@ const onSubmit = () => {
sessionStorage.removeItem('scanReason');
if (isNonEnforcement.value) {
//
router.push({
router.replace({
path: '/enforcement/enterpriseScanPreview',
query: {
type: 'non-enforcement',
@ -513,7 +513,7 @@ const onSubmit = () => {
}
});
} else {
router.push('/enforcement/enforcementList');
router.replace('/enforcement/enforcementList');
}
} else {
showToast(res.msg || '提交失败');

2
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/message/enforcementReserveList.vue

@ -26,7 +26,7 @@
<div class="item-title">待确认预约</div>
<div class="info-row">
<!-- <div class="info-label">检查时间: </div> -->
<div class="info-value">{{ formatTime(item.planDate) }}</div>
<div class="info-value">{{ formatTime(item.reserveTime) }}</div>
</div>
</div>
<div class="item-footer">

2
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/message/enforcementTaskList.vue

@ -27,7 +27,7 @@
<div class="item-title">待执行任务</div>
<div class="info-row">
<!-- <div class="info-label">检查时间: </div> -->
<div class="info-value">{{ item.planDate || '-' }}</div>
<div class="info-value">{{ item.createTime || '-' }}</div>
</div>
</div>
<div class="item-footer">

312
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/notice/cp_notice_message copy.vue

@ -0,0 +1,312 @@
<template>
<div class="notice-message-page app_content">
<!-- 保留一份反馈记录 -->
<!-- 顶部导航 -->
<!-- <van-nav-bar
title="预告留言"
left-arrow
@click-left="onBack"
/> -->
<!-- 消息列表区域 -->
<div class="message-area" ref="messageAreaRef">
<div v-if="loading" class="loading-wrap">
<van-loading size="18" />&nbsp;加载中...
</div>
<template v-else>
<van-empty v-if="!messageList.length" description="暂无留言" image-size="100" />
<div
v-for="(msg, idx) in messageList"
:key="msg.id || idx"
class="message-item"
:class="{ 'message-right': msg.msgFrom === 0, 'message-left': msg.msgFrom === 1 }"
>
<!-- 左侧企业消息 -->
<div v-if="msg.msgFrom === 1" class="msg-avatar left-avatar"></div>
<div class="msg-bubble" :class="{ 'bubble-left': msg.msgFrom === 1, 'bubble-right': msg.msgFrom === 0 }">
<div class="msg-sender">{{ msg.msgFrom === 0 ? (msg.bureauDeptName || useCommon.bureauUnitName || '执法单位') : msg.userName }}</div>
<div class="msg-content">{{ msg.message }}</div>
<div class="msg-time">{{ formatTime(msg.createTime) }}</div>
</div>
<!-- 右侧执法单位消息 -->
</div>
</template>
</div>
<!-- 底部输入区域非只读时显示 -->
<div v-if="!isReadonly" class="input-area">
<van-field
v-model="inputMessage"
type="textarea"
placeholder="输入留言内容..."
rows="1"
autosize
class="input-field"
/>
<van-button
type="primary"
size="small"
class="send-btn"
:disabled="!inputMessage.trim()"
:loading="sending"
@click="onSend"
>
留言
</van-button>
</div>
</div>
</template>
<script setup name="cpNoticeMessage">
import { useRouter, useRoute } from 'vue-router';
import { showToast } from 'vant';
import { getNoticeMessages, addNoticeMessage } from '@/api/enforcement/notice';
const router = useRouter();
const route = useRoute();
const { useCommon } = $globalStore;
const noticeId = computed(() => route.query.id || '');
const isReadonly = computed(() => route.query.readonly === '1');
const messageList = ref([]);
const loading = ref(false);
const inputMessage = ref('');
const sending = ref(false);
const messageAreaRef = ref(null);
let pollTimer = null;
onMounted(() => {
loadMessages();
startPolling();
});
onUnmounted(() => {
stopPolling();
});
const startPolling = () => {
stopPolling();
pollTimer = setInterval(() => {
pollMessages();
}, 16000);
};
const stopPolling = () => {
if (pollTimer) {
clearInterval(pollTimer);
pollTimer = null;
}
};
// loading
const pollMessages = () => {
if (!noticeId.value) return;
getNoticeMessages(noticeId.value, (res) => {
if (res.code === 200 && Array.isArray(res.data)) {
const oldCount = messageList.value.length;
messageList.value = res.data;
if (res.data.length > oldCount) {
scrollToBottom();
}
}
});
};
const loadMessages = () => {
if (!noticeId.value) return;
loading.value = true;
getNoticeMessages(noticeId.value, (res) => {
loading.value = false;
if (res.code === 200 && Array.isArray(res.data)) {
messageList.value = res.data;
scrollToBottom();
}
}, () => {
loading.value = false;
});
};
const onSend = () => {
const text = inputMessage.value.trim();
if (!text) return;
sending.value = true;
addNoticeMessage({
noticeId: noticeId.value,
userId: String(useCommon.userId || ''),
userName: useCommon.nickName || useCommon.userName || '',
bureauDeptName: useCommon.bureauUnitName || '',
msgFrom: 0, // =0
message: text,
}, (res) => {
sending.value = false;
if (res.code === 200) {
inputMessage.value = '';
loadMessages();
} else {
showToast(res.msg || '发送失败');
}
}, () => {
sending.value = false;
showToast('发送失败,请重试');
});
};
const scrollToBottom = () => {
nextTick(() => {
if (messageAreaRef.value) {
messageAreaRef.value.scrollTop = messageAreaRef.value.scrollHeight;
}
});
};
const formatTime = (dateStr) => {
if (!dateStr) return '';
return String(dateStr).substring(0, 16).replace('T', ' ');
};
const onBack = () => {
router.back();
};
</script>
<style lang="less" scoped>
.notice-message-page {
display: flex;
flex-direction: column;
height: 100%;
background: #fbfdff;
overflow: hidden;
}
:deep(.van-nav-bar) {
background: #347bc3;
flex-shrink: 0;
.van-nav-bar__title { color: #fff; font-size: 18px; font-weight: 600; }
.van-icon { color: #fff; }
}
.message-area {
flex: 1;
overflow-y: auto;
padding: 16px 12px;
}
.loading-wrap {
display: flex;
align-items: center;
justify-content: center;
padding: 30px 0;
color: #999;
font-size: 14px;
}
.message-item {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
}
.message-left {
flex-direction: row;
}
.message-right {
flex-direction: row-reverse;
}
.msg-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: 600;
color: #fff;
flex-shrink: 0;
}
.left-avatar {
background: linear-gradient(135deg, #f5a623, #f7c948);
margin-right: 10px;
}
.right-avatar {
background: linear-gradient(135deg, #1890ff, #0b63d1);
margin-left: 10px;
}
.msg-bubble {
max-width: 70%;
padding: 10px 14px;
border-radius: 12px;
position: relative;
}
.bubble-left {
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
border-top-left-radius: 4px;
}
.bubble-right {
background: linear-gradient(135deg, #1890ff, #0b63d1);
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
border-top-right-radius: 4px;
.msg-sender { color: rgba(255, 255, 255, 0.8); }
.msg-content { color: #fff; }
.msg-time { color: rgba(255, 255, 255, 0.6); }
}
.msg-sender {
font-size: 12px;
color: #999;
margin-bottom: 4px;
}
.msg-content {
font-size: 15px;
color: #333;
line-height: 1.5;
word-break: break-all;
}
.msg-time {
font-size: 11px;
color: #bbb;
margin-top: 4px;
text-align: right;
}
.input-area {
display: flex;
align-items: flex-end;
gap: 10px;
padding: 10px 12px;
background: #fff;
border-top: 1px solid #e4edf7;
flex-shrink: 0;
.input-field {
flex: 1;
background: #fbfdff;
border-radius: 20px;
padding: 8px 14px;
:deep(.van-field__control) {
min-height: 24px;
}
}
.send-btn {
height: 36px;
border-radius: 18px;
padding: 0 18px;
font-size: 14px;
background: linear-gradient(135deg, #1890ff, #0b63d1);
border: none;
}
}
</style>

509
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/notice/cp_notice_message.vue

@ -1,65 +1,88 @@
<template>
<div class="notice-message-page app_content">
<!-- 顶部导航 -->
<van-nav-bar
title="预告留言"
left-arrow
@click-left="onBack"
/>
<!-- 消息列表区域 -->
<div class="message-area" ref="messageAreaRef">
<div v-if="loading" class="loading-wrap">
<van-loading size="18" />&nbsp;加载中...
</div>
<template v-else>
<van-empty v-if="!messageList.length" description="暂无留言" image-size="100" />
<div class="chat-page">
<!-- 消息列表 -->
<div class="message-list" ref="messageListRef">
<div
v-for="(msg, index) in messageList"
:key="msg.id || index"
class="message-group"
>
<!-- 时间分割线 -->
<div
v-for="(msg, idx) in messageList"
:key="msg.id || idx"
class="message-item"
:class="{ 'message-right': msg.msgFrom === 0, 'message-left': msg.msgFrom === 1 }"
v-if="shouldShowTime(index)"
class="time-divider"
>
{{ msg.time }}
</div>
<!-- 消息行 -->
<div
class="message-row"
:class="{ 'self': msg.msgFrom === 0 }"
>
<!-- 左侧企业消息 -->
<div v-if="msg.msgFrom === 1" class="msg-avatar left-avatar"></div>
<div class="msg-bubble" :class="{ 'bubble-left': msg.msgFrom === 1, 'bubble-right': msg.msgFrom === 0 }">
<div class="msg-sender">{{ msg.msgFrom === 0 ? (msg.bureauDeptName || useCommon.bureauUnitName || '执法单位') : msg.userName }}</div>
<div class="msg-content">{{ msg.message }}</div>
<div class="msg-time">{{ formatTime(msg.createTime) }}</div>
<div class="message-content">
<div
v-if="msg.isRecalled"
class="message-bubble recalled"
>
{{ msg.msgFrom === 0 ? '你撤回了一条消息' : '对方撤回了一条消息' }}
</div>
<div
v-else
class="message-bubble"
:class="{ 'self': msg.msgFrom === 0, 'other': msg.msgFrom === 1 }"
@contextmenu.prevent="onBubbleContextMenu($event, msg, index)"
@touchstart="onTouchStart($event, msg, index)"
@touchend="onTouchEnd"
@touchmove="onTouchMove"
>
<div class="msg-sender">{{ msg.msgFrom === 0 ? (msg.bureauDeptName || useCommon.bureauUnitName || '执法单位') : msg.userName }}</div>
<span>{{ msg.message }}</span>
</div>
</div>
<!-- 右侧执法单位消息 -->
<div v-if="msg.msgFrom === 0" class="msg-avatar right-avatar"></div>
</div>
</template>
</div>
</div>
<!-- 长按/右击菜单 -->
<div
v-if="menuVisible"
class="menu-overlay"
@click="closeMenu"
@contextmenu.prevent
>
</div>
<!-- 底部输入区域非只读时显示 -->
<div v-if="!isReadonly" class="input-area">
<van-field
v-model="inputMessage"
type="textarea"
placeholder="输入留言内容..."
rows="1"
autosize
class="input-field"
/>
<van-button
type="primary"
size="small"
class="send-btn"
:disabled="!inputMessage.trim()"
:loading="sending"
@click="onSend"
>
留言
</van-button>
<div v-if="!isReadonly" class="chat-input-area">
<div v-if="editMode" class="edit-tip">
正在编辑消息
<span class="edit-cancel" @click="cancelEdit">取消</span>
</div>
<div class="input-wrapper">
<input
v-model="inputMessage"
type="text"
class="chat-input"
:placeholder="editMode ? '编辑消息...' : '请输入消息...'"
@keyup.enter="sendMessage"
/>
<div class="send-btn" @click="sendMessage">
<van-icon :name="editMode ? 'success' : 'arrow-up'" size="18" color="#fff" />
</div>
</div>
</div>
</div>
</template>
<script setup name="cpNoticeMessage">
import { ref, computed, nextTick, onMounted, onUnmounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { showToast } from 'vant';
import { getNoticeMessages, addNoticeMessage } from '@/api/enforcement/notice';
import 'vant/es/toast/style';
import { getNoticeMessages, addNoticeMessage, messageRead } from '@/api/enforcement/notice';
const router = useRouter();
const route = useRoute();
@ -69,10 +92,8 @@ const noticeId = computed(() => route.query.id || '');
const isReadonly = computed(() => route.query.readonly === '1');
const messageList = ref([]);
const loading = ref(false);
const inputMessage = ref('');
const sending = ref(false);
const messageAreaRef = ref(null);
const messageListRef = ref(null);
let pollTimer = null;
onMounted(() => {
@ -84,6 +105,33 @@ onUnmounted(() => {
stopPolling();
});
// ========== ==========
const loadMessages = () => {
if (!noticeId.value) return;
getNoticeMessages(noticeId.value, (res) => {
if (res && res.code === 200 && Array.isArray(res.data)) {
const lastItem = res.data[res.data.length - 1];
messageList.value = res.data.map(item => ({
id: item.id,
msgFrom: item.msgFrom ?? 0,
content: item.content || item.message || '',
message: item.content || item.message || '',
time: item.createTime || item.time || '',
isRecalled: item.isRecalled || false,
userName: item.userName || '',
bureauDeptName: item.bureauDeptName || '',
}));
nextTick(() => scrollToBottom());
if (lastItem && lastItem.id) {
messageRead(lastItem.id, () => {}, () => {});
}
}
}, () => {
showToast('加载消息失败');
});
};
// ========== ==========
const startPolling = () => {
stopPolling();
pollTimer = setInterval(() => {
@ -98,13 +146,21 @@ const stopPolling = () => {
}
};
// loading
const pollMessages = () => {
if (!noticeId.value) return;
getNoticeMessages(noticeId.value, (res) => {
if (res.code === 200 && Array.isArray(res.data)) {
const oldCount = messageList.value.length;
messageList.value = res.data;
messageList.value = res.data.map(item => ({
id: item.id,
msgFrom: item.msgFrom ?? 0,
content: item.content || item.message || '',
message: item.content || item.message || '',
time: item.createTime || item.time || '',
isRecalled: item.isRecalled || false,
userName: item.userName || '',
bureauDeptName: item.bureauDeptName || '',
}));
if (res.data.length > oldCount) {
scrollToBottom();
}
@ -112,106 +168,251 @@ const pollMessages = () => {
});
};
const loadMessages = () => {
if (!noticeId.value) return;
loading.value = true;
getNoticeMessages(noticeId.value, (res) => {
loading.value = false;
if (res.code === 200 && Array.isArray(res.data)) {
messageList.value = res.data;
scrollToBottom();
// ========== ==========
const menuVisible = ref(false);
const menuPosition = ref({ x: 0, y: 0 });
const selectedMsg = ref(null);
const selectedIndex = ref(-1);
// ========== ==========
const editMode = ref(false);
const editingMsgId = ref(null);
// ========== ==========
let touchTimer = null;
let touchMoved = false;
// ========== 线 ==========
const shouldShowTime = (index) => {
if (index === 0) return true;
const prev = messageList.value[index - 1];
const curr = messageList.value[index];
return prev.time !== curr.time;
};
// ========== / ==========
const canEditOrRecall = (msg, index) => {
if (msg.msgFrom !== 0) return false;
if (msg.isRecalled) return false;
const msgTime = new Date(msg.time.replace(/-/g, '/'));
const now = new Date();
const diffMinutes = (now - msgTime) / (1000 * 60);
if (diffMinutes > 3) return false;
const hasOtherReply = messageList.value.slice(index + 1).some(m => m.msgFrom === 1);
if (hasOtherReply) return false;
return true;
};
// ========== PC ==========
const onBubbleContextMenu = (event, msg, index) => {
if (!canEditOrRecall(msg, index)) {
showToast('该消息无法编辑或撤回');
return;
}
selectedMsg.value = msg;
selectedIndex.value = index;
const rect = event.currentTarget.getBoundingClientRect();
menuPosition.value = {
x: rect.left + rect.width / 2,
y: rect.top - 10,
};
menuVisible.value = true;
};
// ========== ==========
const onTouchStart = (event, msg, index) => {
touchMoved = false;
const target = event.currentTarget;
touchTimer = setTimeout(() => {
if (!touchMoved) {
if (!canEditOrRecall(msg, index)) {
showToast('该消息无法编辑或撤回');
return;
}
selectedMsg.value = msg;
selectedIndex.value = index;
const rect = target.getBoundingClientRect();
menuPosition.value = {
x: rect.left + rect.width / 2,
y: rect.top - 10,
};
menuVisible.value = true;
}
}, () => {
loading.value = false;
}, 600);
};
const onTouchEnd = () => {
if (touchTimer) {
clearTimeout(touchTimer);
touchTimer = null;
}
};
const onTouchMove = () => {
touchMoved = true;
if (touchTimer) {
clearTimeout(touchTimer);
touchTimer = null;
}
};
// ========== ==========
const closeMenu = () => {
menuVisible.value = false;
selectedMsg.value = null;
selectedIndex.value = -1;
};
// ========== ==========
const onEdit = () => {
if (!selectedMsg.value) return;
editMode.value = true;
editingMsgId.value = selectedMsg.value.id;
inputMessage.value = selectedMsg.value.message;
closeMenu();
nextTick(() => {
const input = document.querySelector('.chat-input');
if (input) input.focus();
});
};
const onSend = () => {
// ========== ==========
const onRecall = () => {
if (selectedIndex.value === -1) return;
messageList.value[selectedIndex.value].isRecalled = true;
showToast('消息已撤回');
closeMenu();
};
// ========== ==========
const cancelEdit = () => {
editMode.value = false;
editingMsgId.value = null;
inputMessage.value = '';
};
// ========== / ==========
const sendMessage = () => {
const text = inputMessage.value.trim();
if (!text) return;
sending.value = true;
if (!text) {
showToast('请输入消息内容');
return;
}
if (editMode.value && editingMsgId.value !== null) {
const msg = messageList.value.find(m => m.id === editingMsgId.value);
if (msg) {
msg.message = text;
msg.content = text;
showToast('消息已修改');
}
cancelEdit();
return;
}
addNoticeMessage({
noticeId: noticeId.value,
userId: String(useCommon.userId || ''),
userName: useCommon.nickName || useCommon.userName || '',
bureauDeptName: useCommon.bureauUnitName || '',
msgFrom: 0, // =0
msgFrom: 0,
message: text,
}, (res) => {
sending.value = false;
if (res.code === 200) {
if (res && res.code === 200) {
inputMessage.value = '';
loadMessages();
} else {
showToast(res.msg || '发送失败');
}
}, () => {
sending.value = false;
showToast('发送失败,请重试');
});
};
// ========== ==========
const scrollToBottom = () => {
nextTick(() => {
if (messageAreaRef.value) {
messageAreaRef.value.scrollTop = messageAreaRef.value.scrollHeight;
}
});
const el = messageListRef.value;
if (el) {
el.scrollTop = el.scrollHeight;
}
};
const formatTime = (dateStr) => {
if (!dateStr) return '';
return String(dateStr).substring(0, 16).replace('T', ' ');
};
const onBack = () => {
router.back();
};
</script>
<style lang="less" scoped>
.notice-message-page {
.chat-page {
display: flex;
flex-direction: column;
height: 100%;
background: #fbfdff;
overflow: hidden;
}
:deep(.van-nav-bar) {
background: #347bc3;
flex-shrink: 0;
.van-nav-bar__title { color: #fff; font-size: 18px; font-weight: 600; }
.van-icon { color: #fff; }
background-color: #e8f0f8;
position: relative;
}
.message-area {
.message-list {
flex: 1;
overflow-y: auto;
padding: 16px 12px;
padding: 0 0.3rem 0.3rem;
}
.loading-wrap {
display: flex;
align-items: center;
justify-content: center;
padding: 30px 0;
.message-group {
margin-bottom: 0.1rem;
}
.time-divider {
text-align: center;
font-size: 0.24rem;
color: #999;
font-size: 14px;
padding: 0.2rem 0;
}
.message-item {
.message-row {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
margin-bottom: 0.2rem;
&.self {
justify-content: flex-end;
}
}
.message-left {
flex-direction: row;
.message-content {
max-width: 70%;
margin: 0 0.2rem;
}
.message-right {
flex-direction: row-reverse;
.message-bubble {
padding: 0.22rem 0.28rem;
border-radius: 0.16rem;
font-size: 0.32rem;
line-height: 1.5;
word-break: break-word;
user-select: none;
-webkit-user-select: none;
&.self {
background-color: #4485f5;
color: #fff;
border-bottom-right-radius: 0.04rem;
.msg-sender { color: rgba(255, 255, 255, 0.8); }
}
&.other {
background-color: #fff;
color: #333;
border-bottom-left-radius: 0.04rem;
}
&.recalled {
background-color: rgba(0, 0, 0, 0.05);
color: #999;
font-size: 0.28rem;
text-align: center;
border-radius: 0.08rem;
}
.msg-sender { color: #ccc; }
}
.msg-avatar {
@ -229,83 +430,75 @@ const onBack = () => {
.left-avatar {
background: linear-gradient(135deg, #f5a623, #f7c948);
margin-right: 10px;
}
.right-avatar {
background: linear-gradient(135deg, #1890ff, #0b63d1);
margin-left: 10px;
}
.msg-bubble {
max-width: 70%;
padding: 10px 14px;
border-radius: 12px;
position: relative;
.menu-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999;
}
.bubble-left {
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
border-top-left-radius: 4px;
.chat-input-area {
background-color: #fff;
padding: 0.2rem 0.3rem;
border-top: 1px solid #e5e5e5;
}
.bubble-right {
background: linear-gradient(135deg, #1890ff, #0b63d1);
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
border-top-right-radius: 4px;
.msg-sender { color: rgba(255, 255, 255, 0.8); }
.msg-content { color: #fff; }
.msg-time { color: rgba(255, 255, 255, 0.6); }
}
.edit-tip {
font-size: 0.26rem;
color: #4485f5;
margin-bottom: 0.1rem;
display: flex;
align-items: center;
justify-content: space-between;
.msg-sender {
font-size: 12px;
color: #999;
margin-bottom: 4px;
.edit-cancel {
color: #999;
cursor: pointer;
}
}
.msg-content {
font-size: 15px;
color: #333;
line-height: 1.5;
word-break: break-all;
.input-wrapper {
display: flex;
align-items: center;
gap: 0.2rem;
}
.msg-time {
font-size: 11px;
color: #bbb;
margin-top: 4px;
text-align: right;
.chat-input {
flex: 1;
height: 0.8rem;
border: 1px solid #d9d9d9;
border-radius: 0.4rem;
padding: 0 0.3rem;
font-size: 0.32rem;
outline: none;
background-color: #fff;
&:focus {
border-color: #4485f5;
}
}
.input-area {
.send-btn {
width: 0.8rem;
height: 0.8rem;
border-radius: 50%;
background-color: #4485f5;
display: flex;
align-items: flex-end;
gap: 10px;
padding: 10px 12px;
background: #fff;
border-top: 1px solid #e4edf7;
align-items: center;
justify-content: center;
flex-shrink: 0;
cursor: pointer;
.input-field {
flex: 1;
background: #fbfdff;
border-radius: 20px;
padding: 8px 14px;
:deep(.van-field__control) {
min-height: 24px;
}
}
.send-btn {
height: 36px;
border-radius: 18px;
padding: 0 18px;
font-size: 14px;
background: linear-gradient(135deg, #1890ff, #0b63d1);
border: none;
&:active {
opacity: 0.8;
}
}
</style>

8
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/statistics/statisticsAnalysis.vue

@ -409,12 +409,12 @@ const loadStatistics = () => {
const startNumberAnimation = () => {
const duration = 1000;
const startTime = performance.now();
// const targetTotal = statistics.value.registrationTotal; //
const targetTotal = 1; //
const targetTotal = statistics.value.registrationTotal; //
// const targetTotal = 1; //
const targetNotice = statistics.value.noticeTotal; //
const targetAuth = statistics.value.authTotal; //
// const targetEvaluate = statistics.value.evaluateTotal; //
const targetEvaluate = 1; //
const targetEvaluate = statistics.value.evaluateTotal; //
// const targetEvaluate = 1; //
const targetComplaint = statistics.value.complaintTotal; //
const animate = (currentTime) => {

Loading…
Cancel
Save