隐患详情重新设计,检查计划重新设计成做题排查式

This commit is contained in:
王利强
2026-06-21 16:30:12 +08:00
parent 1fe87ec438
commit cc94d3e3e9
256 changed files with 12542 additions and 5956 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,72 @@
import { buildAttachmentItem } from '@/utils/upload.js';
export const LEVEL_OPTIONS = [
{ id: 2, title: '一般隐患' },
{ id: 3, title: '重大隐患' }
];
export const SOURCE_OPTIONS = [
{ id: 1, title: '部门检查' },
{ id: 2, title: '督导检查' },
{ id: 3, title: '企业自查' },
{ id: 4, title: '行业互查' }
];
export const AI_LEVEL_MAP = {
轻微: 0,
轻微隐患: 0,
一般: 0,
一般隐患: 0,
重大: 1,
重大隐患: 1
};
export function createEmptyHazardPayload() {
return {
formData: {
title: '',
level: 0,
source: 0,
description: '',
tagIndex: 0,
tagId: null,
regulationId: null,
regulationName: ''
},
address: '',
lng: 0,
lat: 0,
areaId: '',
areaName: '',
fileList: []
};
}
export function buildHiddenDangerParams(payload, extra = {}) {
const { formData, address, lng, lat, areaId, fileList } = payload;
const selectedTag = payload.tagOptions?.[formData.tagIndex];
const tagId = formData.tagId ?? (selectedTag ? selectedTag.id : null);
return {
title: formData.title,
level: LEVEL_OPTIONS[formData.level]?.id || 2,
lng: lng || 0,
lat: lat || 0,
address: address || '',
areaId: areaId || null,
description: formData.description || '',
source: SOURCE_OPTIONS[formData.source]?.title || '',
tagId,
attachments: (fileList || [])
.filter((f) => f.status === 'success')
.map((file) => buildAttachmentItem(file)),
regulationId: formData.regulationId || null,
...extra
};
}
export function hasValidHazardPayload(payload) {
if (!payload) return false;
const { formData, fileList } = payload;
return !!(formData?.title && fileList?.length > 0);
}

View File

@@ -0,0 +1 @@
export * from './hazardForm.js';

View File

@@ -0,0 +1,760 @@
<template>
<view class="hazard-detail-panel">
<view class="status-bar">
<view class="hazard-title">{{ detail.title || '-' }}</view>
<view class="status-row">
<view class="status-meta">
<text class="info-label">隐患状态</text>
<view :class="['status-tag', statusClass]">{{ detail.statusName || '-' }}</view>
</view>
<text class="info-item info-item--right">提交日期 {{ detail.createdAt || '-' }}</text>
</view>
</view>
<view v-if="loading" class="loading-wrap">
<text class="loading-text">加载中...</text>
</view>
<view v-else-if="historyList.length === 0" class="empty-wrap">
<text class="empty-text">暂无历史记录</text>
</view>
<view v-else class="detail-body">
<!-- 左侧步骤与右侧当前节点联动 -->
<scroll-view
class="steps-column"
scroll-y
:show-scrollbar="false"
:scroll-into-view="stepScrollIntoView"
:scroll-with-animation="true"
>
<view
v-for="(item, index) in historyList"
:key="'step-' + index"
:id="'hazard-step-' + index"
class="step-item"
:class="{ 'step-item--active': activeIndex === index }"
@tap="scrollToNode(index)"
>
<view class="step-track">
<view class="step-dot" :class="{ 'step-dot--active': activeIndex === index }">
<text class="step-dot-text">{{ item.nodeName }}</text>
</view>
<view v-if="index < historyList.length - 1" class="step-line"></view>
</view>
</view>
</scroll-view>
<!-- 右侧卡片连续滚动滚过当前节点后左侧自动切换 -->
<scroll-view
class="content-column content-scroll"
scroll-y
:show-scrollbar="false"
:scroll-into-view="contentScrollIntoView"
:scroll-with-animation="scrollWithAnimation"
@scroll="onContentScroll"
@scrolltolower="onScrollToLower"
>
<view
v-for="(item, index) in historyList"
:key="'node-' + index"
:id="'hazard-node-' + index"
class="node-section"
:class="{ 'node-section--last': index === historyList.length - 1 }"
>
<view class="detail-card">
<view class="card-header">
<!-- <text class="card-node-name">{{ item.nodeName }}</text> -->
<text class="operator">{{ item.titlePrefix }}人员{{ item.operator }}</text>
<text class="time">{{ item.time }}</text>
</view>
<view class="card-body">
<!-- 提交 -->
<block v-if="item.type === 'submit'">
<view class="detail-row">
<text class="label">隐患标题</text>
<text class="value">{{ item.content.title }}</text>
</view>
<view class="detail-row">
<text class="label">检查形式</text>
<text class="value">{{ item.content.source }}</text>
</view>
<view class="detail-row">
<text class="label">隐患区域</text>
<view class="value value--inline">
<view
v-if="item.content.areaColor"
class="area-dot"
:style="{ backgroundColor: item.content.areaColor }"
></view>
<text>{{ item.content.areaName }}</text>
</view>
</view>
<view class="detail-row">
<text class="label">位置描述</text>
<text class="value">{{ item.content.address }}</text>
</view>
<view class="detail-row">
<text class="label">隐患等级</text>
<view class="value">
<view :class="['level-tag', getLevelTagClass(item.content)]">
{{ item.content.levelName || '-' }}
</view>
</view>
</view>
<view class="detail-row">
<text class="label">隐患标签</text>
<text class="value">{{ item.content.tagName }}</text>
</view>
<view class="detail-row">
<text class="label">问题描述</text>
<text class="value">{{ item.content.description }}</text>
</view>
<view
v-if="item.content.attachments && item.content.attachments.length"
class="detail-row detail-row--block"
>
<text class="label">隐患附件</text>
<view class="attachment-list">
<image
v-for="(file, idx) in item.content.attachments"
:key="idx"
class="attachment-img"
:src="resolveFileUrl(file.filePath)"
mode="aspectFill"
@tap="previewImages(item.content.attachments, idx)"
@load="scheduleMeasureLayout"
/>
</view>
</view>
<view class="detail-row">
<text class="label">参考法规</text>
<text class="value">{{ item.content.legalBasis }}</text>
</view>
</block>
<!-- 交办 -->
<block v-else-if="item.type === 'assign'">
<view class="detail-row">
<text class="label">指定整改责任人</text>
<text class="value">{{ item.content.assigneeName }}</text>
</view>
<view class="detail-row">
<text class="label">指定整改截至日期</text>
<text class="value">{{ item.content.deadline }}</text>
</view>
</block>
<!-- 整改 -->
<block v-else-if="item.type === 'rectify'">
<view class="detail-row">
<text class="label">整改状态</text>
<text class="value">{{ item.content.rectifyStatusName }}</text>
</view>
<view class="detail-row">
<text class="label">整改方案</text>
<text class="value">{{ item.content.rectifyPlan }}</text>
</view>
<view class="detail-row">
<text class="label">整改结果</text>
<text class="value">{{ item.content.rectifyResult }}</text>
</view>
<view class="detail-row">
<text class="label">整改措施</text>
<text class="value">{{ item.content.rectificationMeasures }}</text>
</view>
<view class="detail-row">
<text class="label">管控措施</text>
<text class="value">{{ item.content.controlMeasures }}</text>
</view>
<view class="detail-row">
<text class="label">限定整改时间</text>
<text class="value">{{ item.content.deadline }}</text>
</view>
<view class="detail-row">
<text class="label">整改责任人</text>
<text class="value">{{ item.content.rectifierNames }}</text>
</view>
<view class="detail-row">
<text class="label">管理人员</text>
<text class="value">{{ item.content.managerNames }}</text>
</view>
<view class="detail-row">
<text class="label">预计费用</text>
<text class="value">{{ formatCost(item.content.planCost) }}</text>
</view>
<view class="detail-row">
<text class="label">实际费用</text>
<text class="value">{{ formatCost(item.content.actualCost) }}</text>
</view>
<view
v-if="item.content.attachments && item.content.attachments.length"
class="detail-row detail-row--block"
>
<text class="label">整改附件</text>
<view class="attachment-list">
<template v-for="(file, idx) in item.content.attachments" :key="idx">
<image
v-if="file.fileType === 'image'"
class="attachment-img"
:src="resolveFileUrl(file.filePath)"
mode="aspectFill"
@tap="previewImages(item.content.attachments, idx)"
@load="scheduleMeasureLayout"
/>
<text v-else class="file-link">{{ file.fileName || '附件' }}</text>
</template>
</view>
</view>
<view v-if="item.content.signPath" class="detail-row detail-row--block">
<text class="label">整改签字</text>
<image
class="sign-img"
:src="resolveFileUrl(item.content.signPath)"
mode="aspectFit"
@tap="previewSingle(item.content.signPath)"
@load="scheduleMeasureLayout"
/>
</view>
</block>
<!-- 验收 -->
<block v-else-if="item.type === 'verify'">
<view class="detail-row">
<text class="label">验收结果</text>
<text
class="result-tag"
:class="item.content.resultName === '通过' ? 'result-tag--pass' : 'result-tag--fail'"
>
{{ item.content.resultName }}
</text>
</view>
<view v-if="item.content.remark" class="detail-row">
<text class="label">验收备注</text>
<text class="value">{{ item.content.remark }}</text>
</view>
<view
v-if="item.content.attachments && item.content.attachments.length"
class="detail-row detail-row--block"
>
<text class="label">验收附件</text>
<view class="attachment-list">
<template v-for="(file, idx) in item.content.attachments" :key="idx">
<image
v-if="file.fileType === 'image'"
class="attachment-img"
:src="resolveFileUrl(file.filePath)"
mode="aspectFill"
@tap="previewImages(item.content.attachments, idx)"
@load="scheduleMeasureLayout"
/>
<text v-else class="file-link">{{ file.fileName || '附件' }}</text>
</template>
</view>
</view>
<view v-if="item.content.signPath" class="detail-row detail-row--block">
<text class="label">验收签字</text>
<image
class="sign-img"
:src="resolveFileUrl(item.content.signPath)"
mode="aspectFit"
@tap="previewSingle(item.content.signPath)"
@load="scheduleMeasureLayout"
/>
</view>
</block>
<!-- 销号 -->
<block v-else-if="item.type === 'writeoff'">
<view class="detail-row">
<text class="label">销号结果</text>
<text
class="result-tag"
:class="item.content.resultName === '通过' ? 'result-tag--pass' : 'result-tag--fail'"
>
{{ item.content.resultName }}
</text>
</view>
<view v-if="item.content.remark" class="detail-row">
<text class="label">销号备注</text>
<text class="value">{{ item.content.remark }}</text>
</view>
<view
v-if="item.content.attachments && item.content.attachments.length"
class="detail-row detail-row--block"
>
<text class="label">销号附件</text>
<view class="attachment-list">
<template v-for="(file, idx) in item.content.attachments" :key="idx">
<image
v-if="file.fileType === 'image'"
class="attachment-img"
:src="resolveFileUrl(file.filePath)"
mode="aspectFill"
@tap="previewImages(item.content.attachments, idx)"
@load="scheduleMeasureLayout"
/>
<text v-else class="file-link">{{ file.fileName || '附件' }}</text>
</template>
</view>
</view>
<view v-if="item.content.signPath" class="detail-row detail-row--block">
<text class="label">销号签字</text>
<image
class="sign-img"
:src="resolveFileUrl(item.content.signPath)"
mode="aspectFit"
@tap="previewSingle(item.content.signPath)"
@load="scheduleMeasureLayout"
/>
</view>
</block>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { computed, toRefs } from 'vue';
import { toImageUrl } from '@/request/request.js';
import { getStatusClass } from './hazardDetail.js';
import { useHazardDetailScroll } from './useHazardDetailScroll.js';
const props = defineProps({
detail: {
type: Object,
default: () => ({})
},
loading: {
type: Boolean,
default: false
}
});
const { detail, loading } = toRefs(props);
const {
historyList,
activeIndex,
contentScrollIntoView,
stepScrollIntoView,
scrollWithAnimation,
onContentScroll,
onScrollToLower,
scrollToNode,
scheduleMeasureLayout
} = useHazardDetailScroll(detail, loading);
const statusClass = computed(() => getStatusClass(props.detail?.status, props.detail?.statusName));
const LEVEL_CLASS_MAP = {
2: 'level-normal',
3: 'level-major'
};
const LEVEL_NAME_CLASS_MAP = {
一般: 'level-normal',
一般隐患: 'level-normal',
重大: 'level-major',
重大隐患: 'level-major'
};
const getLevelTagClass = (content) => {
const level = content?.level;
const levelName = content?.levelName;
if (level != null && LEVEL_CLASS_MAP[level]) {
return LEVEL_CLASS_MAP[level];
}
if (levelName && LEVEL_NAME_CLASS_MAP[levelName]) {
return LEVEL_NAME_CLASS_MAP[levelName];
}
return '';
};
const resolveFileUrl = (path) => toImageUrl(path);
const formatCost = (cost) => {
if (cost == null || cost === '') return '-';
const num = Number(cost);
return Number.isNaN(num) ? '-' : `${num}`;
};
const previewSingle = (path) => {
const url = resolveFileUrl(path);
if (!url) return;
uni.previewImage({ current: url, urls: [url] });
};
const previewImages = (attachments, index) => {
const urls = (attachments || [])
.filter((f) => f.fileType === 'image' || !f.fileType)
.map((f) => resolveFileUrl(f.filePath))
.filter(Boolean);
if (!urls.length) return;
uni.previewImage({ current: urls[index] || urls[0], urls });
};
</script>
<style lang="scss" scoped>
.hazard-detail-panel {
display: flex;
flex-direction: column;
height: 100vh;
background: #ebf2fc;
overflow: hidden;
}
.status-bar {
flex-shrink: 0;
background: #f5f7fa;
border-bottom: 1rpx solid #e4e7ed;
padding: 20rpx 24rpx;
}
.hazard-title {
font-size: 32rpx;
font-weight: 600;
color: #303133;
line-height: 1.4;
margin-bottom: 16rpx;
word-break: break-all;
}
.status-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24rpx;
font-size: 24rpx;
color: #909399;
}
.info-item {
line-height: 1.5;
flex-shrink: 0;
}
.info-item--right {
text-align: right;
}
.status-meta {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
}
.info-label {
line-height: 1.5;
}
.status-tag {
display: inline-flex;
align-items: center;
padding: 4rpx 16rpx;
border-radius: 8rpx;
font-size: 22rpx;
font-weight: 500;
line-height: 1.4;
white-space: nowrap;
}
.status-blue {
background: #ecf5ff;
border: 2rpx solid #b3d8ff;
color: #409eff;
}
.status-green {
background: #f0f9eb;
border: 2rpx solid #c2e7b0;
color: #67c23a;
}
.status-orange {
background: #fff7e6;
border: 2rpx solid #ffd591;
color: #fa8c16;
}
.status-red {
background: #fef0f0;
border: 2rpx solid #fbc4c4;
color: #f56c6c;
}
.status-yellow {
background: #fdf6ec;
border: 2rpx solid #f5dab1;
color: #e6a23c;
}
.status-gray {
background: #f4f4f5;
border: 2rpx solid #d3d4d6;
color: #909399;
}
.level-tag {
display: inline-flex;
align-items: center;
padding: 4rpx 16rpx;
border-radius: 8rpx;
font-size: 24rpx;
font-weight: 500;
line-height: 1.4;
white-space: nowrap;
}
.level-normal {
background: #fffbe6;
border: 2rpx solid #ffe58f;
color: #faad14;
}
.level-major {
background: #fff1f0;
border: 2rpx solid #ffa39e;
color: #f5222d;
}
.loading-wrap,
.empty-wrap {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.loading-text,
.empty-text {
font-size: 28rpx;
color: #909399;
}
.detail-body {
flex: 1;
display: flex;
min-height: 0;
overflow: hidden;
}
.steps-column {
width: 168rpx;
flex-shrink: 0;
height: 100%;
background: #fff;
border-right: 1rpx solid #ebeef5;
}
.step-item {
padding: 0 16rpx;
}
.step-track {
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx 0 0;
}
.step-dot {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
background: #dcdfe6;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
transition: background 0.2s;
}
.step-dot--active {
background: #409eff;
}
.step-dot-text {
font-size: 22rpx;
font-weight: 600;
color: #606266;
text-align: center;
line-height: 1.2;
}
.step-item--active .step-dot-text {
color: #fff;
}
.step-line {
width: 4rpx;
height: 48rpx;
background: #e4e7ed;
margin: 8rpx 0;
}
.content-column {
flex: 1;
min-width: 0;
height: 100%;
box-sizing: border-box;
}
.node-section {
box-sizing: border-box;
padding: 20rpx 20rpx 0;
margin-bottom: 24rpx;
}
.node-section--last {
padding-bottom: 40rpx;
margin-bottom: 0;
}
.detail-card {
background: #fff;
border-radius: 16rpx;
border: 1rpx solid #ebeef5;
overflow: hidden;
}
.card-header {
display: flex;
flex-direction: column;
gap: 8rpx;
padding: 20rpx;
background: linear-gradient(135deg, #4facfe 0%, #2668ea 100%);
}
.card-node-name {
font-size: 30rpx;
font-weight: 600;
color: #fff;
line-height: 1.4;
}
.operator {
font-size: 28rpx;
font-weight: 600;
color: #fff;
line-height: 1.5;
word-break: break-all;
}
.time {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.85);
}
.card-body {
padding: 24rpx;
}
.detail-row {
display: flex;
flex-direction: column;
gap: 8rpx;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
}
.detail-row--block {
margin-bottom: 24rpx;
}
.label {
display: flex;
align-items: center;
gap: 10rpx;
font-size: 24rpx;
color: #909399;
font-weight: 500;
&::before {
content: '';
width: 10rpx;
height: 10rpx;
border-radius: 50%;
background: #909399;
flex-shrink: 0;
}
}
.value {
font-size: 28rpx;
color: #303133;
line-height: 1.6;
word-break: break-all;
}
.value--inline {
display: flex;
align-items: center;
}
.area-dot {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
margin-right: 12rpx;
flex-shrink: 0;
}
.attachment-list {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-top: 8rpx;
}
.attachment-img {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
background: #f5f7fa;
}
.sign-img {
width: 300rpx;
height: 160rpx;
margin-top: 8rpx;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
background: #fafafa;
}
.file-link {
display: inline-block;
padding: 12rpx 20rpx;
background: #f5f7fa;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
color: #409eff;
font-size: 24rpx;
}
.result-tag {
align-self: flex-start;
padding: 6rpx 16rpx;
border-radius: 6rpx;
font-size: 24rpx;
}
.result-tag--pass {
background: #f0f9eb;
color: #67c23a;
}
.result-tag--fail {
background: #fef0f0;
color: #f56c6c;
}
</style>

View File

@@ -0,0 +1,743 @@
<template>
<view class="hazard-detail-panel-v2">
<view v-if="loading" class="loading-wrap">
<text class="loading-text">加载中...</text>
</view>
<view v-else-if="historyList.length === 0" class="empty-wrap">
<text class="empty-text">暂无历史记录</text>
</view>
<view v-else class="detail-body" :style="detailBodyStyle">
<scroll-view
class="steps-card"
scroll-y
:style="scrollAreaStyle"
:show-scrollbar="false"
:scroll-into-view="stepScrollIntoView"
:scroll-with-animation="true"
>
<view
v-for="(item, index) in historyList"
:key="'step-' + index"
:id="'hazard-step-' + index"
class="step-item"
:class="{ 'step-item--active': activeIndex === index }"
@tap="scrollToNode(index)"
>
<view class="step-track">
<view
class="step-dot"
:class="{ 'step-dot--active': activeIndex === index }"
>
<image
class="step-icon"
:src="getStepIconPath(item.type, activeIndex === index)"
mode="aspectFit"
/>
</view>
<text
class="step-label"
:class="{ 'step-label--active': activeIndex === index }"
>{{ item.nodeName }}</text>
<view v-if="index < historyList.length - 1" class="step-line"></view>
</view>
</view>
</scroll-view>
<scroll-view
class="content-column content-scroll"
scroll-y
:style="scrollAreaStyle"
:show-scrollbar="false"
:scroll-into-view="contentScrollIntoView"
:scroll-with-animation="scrollWithAnimation"
@scroll="onContentScroll"
@scrolltolower="onScrollToLower"
>
<view
v-for="(item, index) in historyList"
:key="'node-' + index"
:id="'hazard-node-' + index"
class="node-section"
:class="{ 'node-section--last': index === historyList.length - 1 }"
>
<view class="detail-card">
<view class="card-header-v2">
<view class="card-header-main">
<text class="operator">{{ item.titlePrefix }}人员{{ item.operator }}</text>
<text class="time">{{ item.time }}</text>
</view>
<view
v-if="item.type === 'submit' && item.content.levelName"
:class="['level-badge', getLevelTagClass(item.content)]"
>
{{ item.content.levelName }}
</view>
</view>
<view class="card-divider"></view>
<view class="card-body">
<!-- 提交 -->
<block v-if="item.type === 'submit'">
<view class="detail-row-v2">
<text class="label">隐患标题</text>
<text class="value">{{ item.content.title }}</text>
</view>
<view class="detail-row-v2">
<text class="label">检查形式</text>
<text class="value">{{ item.content.source }}</text>
</view>
<view class="detail-row-v2">
<text class="label">隐患区域</text>
<view class="value value--inline">
<view
v-if="item.content.areaColor"
class="area-dot"
:style="{ backgroundColor: item.content.areaColor }"
></view>
<text>{{ item.content.areaName }}</text>
</view>
</view>
<view class="detail-row-v2">
<text class="label">位置描述</text>
<text class="value">{{ item.content.address }}</text>
</view>
<view class="detail-row-v2">
<text class="label">隐患等级</text>
<view class="value">
<view :class="['level-tag', getLevelTagClass(item.content)]">
{{ item.content.levelName || '-' }}
</view>
</view>
</view>
<view class="detail-row-v2">
<text class="label">隐患标签</text>
<text class="value">{{ item.content.tagName }}</text>
</view>
<view class="detail-row-v2">
<text class="label">问题描述</text>
<text class="value">{{ item.content.description }}</text>
</view>
<view
v-if="item.content.attachments && item.content.attachments.length"
class="detail-row-v2 detail-row-v2--block"
>
<text class="label">隐患附件</text>
<view class="attachment-list">
<image
v-for="(file, idx) in item.content.attachments"
:key="idx"
class="attachment-img"
:src="resolveFileUrl(file.filePath)"
mode="aspectFill"
@tap="previewImages(item.content.attachments, idx)"
@load="scheduleMeasureLayout"
/>
</view>
</view>
<view class="detail-row-v2">
<text class="label">参考法规</text>
<text class="value">{{ item.content.legalBasis }}</text>
</view>
</block>
<!-- 交办 -->
<block v-else-if="item.type === 'assign'">
<view class="detail-row-v2">
<text class="label">指定整改责任人</text>
<text class="value">{{ item.content.assigneeName }}</text>
</view>
<view class="detail-row-v2">
<text class="label">指定整改截至日期</text>
<text class="value">{{ item.content.deadline }}</text>
</view>
</block>
<!-- 整改 -->
<block v-else-if="item.type === 'rectify'">
<view class="detail-row-v2">
<text class="label">整改状态</text>
<text class="value">{{ item.content.rectifyStatusName }}</text>
</view>
<view class="detail-row-v2">
<text class="label">整改方案</text>
<text class="value">{{ item.content.rectifyPlan }}</text>
</view>
<view class="detail-row-v2">
<text class="label">整改结果</text>
<text class="value">{{ item.content.rectifyResult }}</text>
</view>
<view class="detail-row-v2">
<text class="label">整改措施</text>
<text class="value">{{ item.content.rectificationMeasures }}</text>
</view>
<view class="detail-row-v2">
<text class="label">管控措施</text>
<text class="value">{{ item.content.controlMeasures }}</text>
</view>
<view class="detail-row-v2">
<text class="label">限定整改时间</text>
<text class="value">{{ item.content.deadline }}</text>
</view>
<view class="detail-row-v2">
<text class="label">整改责任人</text>
<text class="value">{{ item.content.rectifierNames }}</text>
</view>
<view class="detail-row-v2">
<text class="label">管理人员</text>
<text class="value">{{ item.content.managerNames }}</text>
</view>
<view class="detail-row-v2">
<text class="label">预计费用</text>
<text class="value">{{ formatCost(item.content.planCost) }}</text>
</view>
<view class="detail-row-v2">
<text class="label">实际费用</text>
<text class="value">{{ formatCost(item.content.actualCost) }}</text>
</view>
<view
v-if="item.content.attachments && item.content.attachments.length"
class="detail-row-v2 detail-row-v2--block"
>
<text class="label">整改附件</text>
<view class="attachment-list">
<template v-for="(file, idx) in item.content.attachments" :key="idx">
<image
v-if="file.fileType === 'image'"
class="attachment-img"
:src="resolveFileUrl(file.filePath)"
mode="aspectFill"
@tap="previewImages(item.content.attachments, idx)"
@load="scheduleMeasureLayout"
/>
<text v-else class="file-link">{{ file.fileName || '附件' }}</text>
</template>
</view>
</view>
<view v-if="item.content.signPath" class="detail-row-v2 detail-row-v2--block">
<text class="label">整改签字</text>
<image
class="sign-img"
:src="resolveFileUrl(item.content.signPath)"
mode="aspectFit"
@tap="previewSingle(item.content.signPath)"
@load="scheduleMeasureLayout"
/>
</view>
</block>
<!-- 验收 -->
<block v-else-if="item.type === 'verify'">
<view class="detail-row-v2">
<text class="label">验收结果</text>
<text
class="result-tag"
:class="item.content.resultName === '通过' ? 'result-tag--pass' : 'result-tag--fail'"
>
{{ item.content.resultName }}
</text>
</view>
<view v-if="item.content.remark" class="detail-row-v2">
<text class="label">验收备注</text>
<text class="value">{{ item.content.remark }}</text>
</view>
<view
v-if="item.content.attachments && item.content.attachments.length"
class="detail-row-v2 detail-row-v2--block"
>
<text class="label">验收附件</text>
<view class="attachment-list">
<template v-for="(file, idx) in item.content.attachments" :key="idx">
<image
v-if="file.fileType === 'image'"
class="attachment-img"
:src="resolveFileUrl(file.filePath)"
mode="aspectFill"
@tap="previewImages(item.content.attachments, idx)"
@load="scheduleMeasureLayout"
/>
<text v-else class="file-link">{{ file.fileName || '附件' }}</text>
</template>
</view>
</view>
<view v-if="item.content.signPath" class="detail-row-v2 detail-row-v2--block">
<text class="label">验收签字</text>
<image
class="sign-img"
:src="resolveFileUrl(item.content.signPath)"
mode="aspectFit"
@tap="previewSingle(item.content.signPath)"
@load="scheduleMeasureLayout"
/>
</view>
</block>
<!-- 销号 -->
<block v-else-if="item.type === 'writeoff'">
<view class="detail-row-v2">
<text class="label">销号结果</text>
<text
class="result-tag"
:class="item.content.resultName === '通过' ? 'result-tag--pass' : 'result-tag--fail'"
>
{{ item.content.resultName }}
</text>
</view>
<view v-if="item.content.remark" class="detail-row-v2">
<text class="label">销号备注</text>
<text class="value">{{ item.content.remark }}</text>
</view>
<view
v-if="item.content.attachments && item.content.attachments.length"
class="detail-row-v2 detail-row-v2--block"
>
<text class="label">销号附件</text>
<view class="attachment-list">
<template v-for="(file, idx) in item.content.attachments" :key="idx">
<image
v-if="file.fileType === 'image'"
class="attachment-img"
:src="resolveFileUrl(file.filePath)"
mode="aspectFill"
@tap="previewImages(item.content.attachments, idx)"
@load="scheduleMeasureLayout"
/>
<text v-else class="file-link">{{ file.fileName || '附件' }}</text>
</template>
</view>
</view>
<view v-if="item.content.signPath" class="detail-row-v2 detail-row-v2--block">
<text class="label">销号签字</text>
<image
class="sign-img"
:src="resolveFileUrl(item.content.signPath)"
mode="aspectFit"
@tap="previewSingle(item.content.signPath)"
@load="scheduleMeasureLayout"
/>
</view>
</block>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { computed, toRefs, watch } from 'vue';
import { toImageUrl } from '@/request/request.js';
import { getStepIconPath } from './hazardDetail.js';
import { useHazardDetailScroll } from './useHazardDetailScroll.js';
const props = defineProps({
detail: {
type: Object,
default: () => ({})
},
loading: {
type: Boolean,
default: false
},
bodyHeight: {
type: Number,
default: 0
}
});
const { detail, loading } = toRefs(props);
const {
historyList,
activeIndex,
contentScrollIntoView,
stepScrollIntoView,
scrollWithAnimation,
onContentScroll,
onScrollToLower,
scrollToNode,
scheduleMeasureLayout
} = useHazardDetailScroll(detail, loading);
const scrollAreaStyle = computed(() => {
const height = props.bodyHeight > 0 ? props.bodyHeight : 400;
return { height: `${height}px` };
});
const detailBodyStyle = computed(() => {
const height = props.bodyHeight > 0 ? props.bodyHeight : 400;
return { height: `${height}px` };
});
watch(
() => props.bodyHeight,
(height) => {
if (height > 0) {
scheduleMeasureLayout();
setTimeout(scheduleMeasureLayout, 100);
}
}
);
const LEVEL_CLASS_MAP = {
2: 'level-normal',
3: 'level-major'
};
const LEVEL_NAME_CLASS_MAP = {
一般: 'level-normal',
一般隐患: 'level-normal',
重大: 'level-major',
重大隐患: 'level-major'
};
const getLevelTagClass = (content) => {
const level = content?.level;
const levelName = content?.levelName;
if (level != null && LEVEL_CLASS_MAP[level]) {
return LEVEL_CLASS_MAP[level];
}
if (levelName && LEVEL_NAME_CLASS_MAP[levelName]) {
return LEVEL_NAME_CLASS_MAP[levelName];
}
return '';
};
const resolveFileUrl = (path) => toImageUrl(path);
const formatCost = (cost) => {
if (cost == null || cost === '') return '-';
const num = Number(cost);
return Number.isNaN(num) ? '-' : `${num}`;
};
const previewSingle = (path) => {
const url = resolveFileUrl(path);
if (!url) return;
uni.previewImage({ current: url, urls: [url] });
};
const previewImages = (attachments, index) => {
const urls = (attachments || [])
.filter((f) => f.fileType === 'image' || !f.fileType)
.map((f) => resolveFileUrl(f.filePath))
.filter(Boolean);
if (!urls.length) return;
uni.previewImage({ current: urls[index] || urls[0], urls });
};
</script>
<style lang="scss" scoped>
.hazard-detail-panel-v2 {
flex: 1;
min-height: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.loading-wrap,
.empty-wrap {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
min-height: 0;
}
.loading-text,
.empty-text {
font-size: 28rpx;
color: #909399;
}
.detail-body {
display: flex;
gap: 20rpx;
align-items: stretch;
overflow: hidden;
box-sizing: border-box;
}
.steps-card {
width: 140rpx;
flex-shrink: 0;
background: #fff;
border-radius: 20rpx;
box-sizing: border-box;
}
.step-item {
padding: 0 12rpx;
}
.step-track {
display: flex;
flex-direction: column;
align-items: center;
padding: 28rpx 0 0;
}
.step-dot {
width: 66rpx;
height: 66rpx;
border-radius: 50%;
background: #f3f3f3;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}
.step-dot--active {
background: #2667e9;
}
.step-icon {
width: 36rpx;
height: 36rpx;
}
.step-label {
margin-top: 12rpx;
font-size: 24rpx;
color: #999;
line-height: 1.2;
}
.step-label--active {
color: #2667e9;
font-weight: 600;
}
.step-line {
width: 0;
height: 40rpx;
margin: 10rpx 0;
border-left: 2rpx dashed #dcdfe6;
}
.content-column {
flex: 1;
width: 0;
min-height: 0;
box-sizing: border-box;
}
.content-scroll {
width: 100%;
box-sizing: border-box;
}
.node-section {
box-sizing: border-box;
padding: 0;
margin-bottom: 24rpx;
}
.node-section--last {
padding-bottom: 40rpx;
margin-bottom: 0;
}
.detail-card {
background: #fff;
border-radius: 20rpx;
overflow: hidden;
padding: 28rpx 38rpx;
box-sizing: border-box;
}
.card-header-v2 {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16rpx;
padding: 0;
}
.card-header-main {
flex: 1;
min-width: 0;
}
.operator {
font-size: 28rpx;
font-weight: 600;
color: #303133;
line-height: 1.5;
word-break: break-all;
}
.time {
display: block;
margin-top: 8rpx;
font-size: 24rpx;
color: #999;
line-height: 1.4;
}
.level-badge {
flex-shrink: 0;
padding: 6rpx 16rpx;
border-radius: 8rpx;
font-size: 22rpx;
font-weight: 500;
white-space: nowrap;
}
.level-badge.level-normal {
background: #fff7e6;
border: 2rpx solid #ffd591;
color: #fa8c16;
}
.level-badge.level-major {
background: #fff1f0;
border: 2rpx solid #ffa39e;
color: #f5222d;
}
.card-divider {
height: 0;
margin: 20rpx 0;
border-top: 2rpx dashed #eee;
}
.card-body {
padding: 0;
}
.detail-row-v2 {
display: flex;
align-items: flex-start;
justify-content: flex-start;
gap: 24rpx;
padding: 18rpx 0;
}
.detail-row-v2--block {
flex-wrap: wrap;
}
.label {
flex-shrink: 0;
// width: 156rpx;
font-size: 26rpx;
color: #999;
line-height: 1.5;
}
.value {
flex: 1;
min-width: 0;
font-size: 26rpx;
color: #333;
line-height: 1.6;
text-align: left;
word-break: break-all;
}
.value--inline {
display: flex;
align-items: center;
}
.area-dot {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
margin-right: 12rpx;
flex-shrink: 0;
}
.level-tag {
display: inline-flex;
align-items: center;
padding: 4rpx 16rpx;
border-radius: 8rpx;
font-size: 24rpx;
font-weight: 500;
line-height: 1.4;
white-space: nowrap;
}
.level-normal {
background: #fff7e6;
border: 2rpx solid #ffd591;
color: #fa8c16;
}
.level-major {
background: #fff1f0;
border: 2rpx solid #ffa39e;
color: #f5222d;
}
.tag-badge {
display: inline-flex;
align-items: center;
padding: 4rpx 16rpx;
border-radius: 8rpx;
font-size: 24rpx;
background: #eef3ff;
border: 2rpx solid #aac5fc;
color: #2667e9;
}
.attachment-list {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
flex: 1;
min-width: 0;
}
.attachment-img {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
background: #f5f7fa;
}
.sign-img {
width: 300rpx;
height: 160rpx;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
background: #fafafa;
flex: 1;
min-width: 0;
max-width: 100%;
}
.file-link {
display: inline-block;
padding: 12rpx 20rpx;
background: #f5f7fa;
border: 1rpx solid #e4e7ed;
border-radius: 8rpx;
color: #2667e9;
font-size: 24rpx;
}
.result-tag {
padding: 6rpx 16rpx;
border-radius: 6rpx;
font-size: 24rpx;
}
.result-tag--pass {
background: #f0f9eb;
color: #67c23a;
}
.result-tag--fail {
background: #fef0f0;
color: #f56c6c;
}
</style>

View File

@@ -0,0 +1,371 @@
/** 隐患详情历史节点类型 */
export const HAZARD_NODE_TYPES = {
SUBMIT: 'submit',
ASSIGN: 'assign',
RECTIFY: 'rectify',
VERIFY: 'verify',
WRITEOFF: 'writeoff'
};
export const HAZARD_STATUS_CLASS_MAP = {
1: 'status-blue',
2: 'status-orange',
3: 'status-red',
4: 'status-yellow',
5: 'status-green'
};
const STATUS_NAME_CLASS_MAP = {
待交办: 'status-blue',
待整改: 'status-orange',
整改中: 'status-orange',
待验收: 'status-red',
待销号: 'status-yellow',
已完成: 'status-green',
已销号: 'status-green'
};
const LEVEL_NAME_MAP = {
2: '一般隐患',
3: '重大隐患'
};
export function getLevelName(level) {
return LEVEL_NAME_MAP[level] || '未知';
}
export function getStatusClass(status, statusName) {
if (statusName && STATUS_NAME_CLASS_MAP[statusName]) {
return STATUS_NAME_CLASS_MAP[statusName];
}
return HAZARD_STATUS_CLASS_MAP[status] || '';
}
const LEVEL_CLASS_MAP = {
2: 'level-normal',
3: 'level-major'
};
const LEVEL_NAME_CLASS_MAP = {
一般: 'level-normal',
一般隐患: 'level-normal',
重大: 'level-major',
重大隐患: 'level-major'
};
export function getStepIconPath(type, active = false) {
const baseMap = {
submit: 'tijiao',
assign: 'jiaoban',
rectify: 'zhenggai',
verify: 'yanshou',
writeoff: 'xiaohao'
};
const base = baseMap[type] || 'tijiao';
if (base === 'zhenggai') {
return active
? '/static/yinhuan_detail/zhenggai_selected.png'
: '/static/yinhuan_detail/zhenggai__unselected.png';
}
return `/static/yinhuan_detail/${base}__${active ? 'selected' : 'unselected'}.png`;
}
export function getLevelClass(level, levelName) {
if (level != null && LEVEL_CLASS_MAP[level]) {
return LEVEL_CLASS_MAP[level];
}
if (levelName && LEVEL_NAME_CLASS_MAP[levelName]) {
return LEVEL_NAME_CLASS_MAP[levelName];
}
return '';
}
/** 将姓名数组或字符串格式化为「a、b、c」 */
export function formatNameList(names, fallback = '-') {
if (Array.isArray(names) && names.length) {
return names.join('、');
}
if (typeof names === 'string' && names.trim()) {
const parts = names.split(/[,,、]/).map((s) => s.trim()).filter(Boolean);
return parts.length ? parts.join('、') : names;
}
return fallback;
}
/** 判断是否为销号记录type=2 或 typeName=销号) */
export function isWriteoffRecord(record) {
if (!record) return false;
return record.type === 2 || record.typeName === '销号';
}
function buildVerifyNode(record) {
return {
type: HAZARD_NODE_TYPES.VERIFY,
nodeName: '验收',
titlePrefix: '验收',
operator: record.verifierName || '-',
time: record.verifyTime || '-',
content: {
resultName: record.resultName || '-',
remark: record.remark || '',
attachments: record.attachments || [],
signPath: record.signPath || '',
verifyDeptName: record.verifyDeptName || '-'
}
};
}
function buildWriteoffNode(record) {
return {
type: HAZARD_NODE_TYPES.WRITEOFF,
nodeName: '销号',
titlePrefix: '销号',
operator: record.verifierName || '-',
time: record.verifyTime || '-',
content: {
resultName: record.resultName || '-',
remark: record.remark || '',
attachments: record.attachments || [],
signPath: record.signPath || '',
writeoffDeptName: record.writeoffDeptName || '-'
}
};
}
/** 合并验收/销号记录,按 verifyId 去重 */
function appendAuditNodes(list, records, seenIds) {
(records || []).forEach((record) => {
if (!record) return;
const key = record.verifyId ?? record.id;
if (key != null) {
if (seenIds.has(key)) return;
seenIds.add(key);
}
list.push(isWriteoffRecord(record) ? buildWriteoffNode(record) : buildVerifyNode(record));
});
}
function resolveWriteoffRecords(flow) {
return flow.writeOffs || flow.writeoffs || [];
}
/** 解析整改责任人(兼容数组、逗号分隔字符串、单人字段) */
export function resolveRectifierNames(rectify) {
if (!rectify) return '-';
if (Array.isArray(rectify.rectifierNames) && rectify.rectifierNames.length) {
return rectify.rectifierNames.join('、');
}
if (Array.isArray(rectify.memberNames) && rectify.memberNames.length) {
return rectify.memberNames.join('、');
}
return formatNameList(rectify.rectifierName, '-');
}
/**
* 将隐患详情转为步骤历史列表(与后台 HazardDetailDrawer 保持一致)
* @param {Object} data 隐患详情
* @returns {Array}
*/
export function generateHazardHistory(data) {
if (!data) return [];
const list = [];
list.push({
type: HAZARD_NODE_TYPES.SUBMIT,
nodeName: '提交',
titlePrefix: '提交',
operator: data.reporterName || '-',
time: data.createdAt || '-',
content: {
title: data.title || '-',
source: data.source || '-',
areaName: data.areaName || '-',
areaColor: data.areaColor || '',
address: data.address || '-',
level: data.level,
levelName: data.levelName || getLevelName(data.level),
tagName: data.tagName || '-',
description: data.description || '-',
attachments: data.attachments || [],
legalBasis: data.legalBasis || data.regulationName || '-',
reporterPhone: data.reporterPhone || '-',
reportDeptName: data.reportDeptName || '-'
}
});
if (data.assignFlows && data.assignFlows.length > 0) {
data.assignFlows.forEach((flow) => {
if (flow.assignId) {
list.push({
type: HAZARD_NODE_TYPES.ASSIGN,
nodeName: '交办',
titlePrefix: '交办',
operator: flow.assignerName || '-',
time: flow.assignTime || '-',
content: {
assigneeName: flow.assigneeName || '-',
deadline: flow.deadline || '-',
assignDeptName: flow.assignDeptName || '-',
assignRemark: flow.assignRemark || '-',
priorityName: flow.priorityName || '-'
}
});
}
if (flow.rectify) {
list.push({
type: HAZARD_NODE_TYPES.RECTIFY,
nodeName: '整改',
titlePrefix: '整改',
operator: flow.rectify.rectifierName || '-',
time: flow.rectify.rectifyTime || '-',
content: {
rectifyPlan: flow.rectify.rectifyPlan || '-',
rectifyResult: flow.rectify.rectifyResult || '-',
rectificationMeasures: flow.rectify.rectificationMeasures || '-',
controlMeasures: flow.rectify.controlMeasures || '-',
deadline: flow.deadline || '-',
rectifierNames: resolveRectifierNames(flow.rectify),
managerNames: formatNameList(flow.rectify.managerNames, '-'),
planCost: flow.rectify.planCost,
actualCost: flow.rectify.actualCost,
attachments: flow.rectify.attachments || [],
signPath: flow.rectify.signPath || '',
rectifyStatusName: flow.rectify.rectifyStatusName || '-'
}
});
}
const seenAuditIds = new Set();
appendAuditNodes(list, flow.verifies || [], seenAuditIds);
appendAuditNodes(list, resolveWriteoffRecords(flow), seenAuditIds);
});
}
return list;
}
const LONG_DESCRIPTION =
'消防通道内堆放大量纸箱及杂物,影响疏散通道畅通,存在火灾安全隐患。' +
'现场检查发现通道宽度不足1.2米,部分区域被临时物料占用,紧急情况下可能影响人员撤离。' +
'建议立即清理并建立日常巡查机制,防止问题反弹。';
/** 开发联调前使用的 mock 详情数据(各节点均含完整假数据) */
export const MOCK_HAZARD_DETAIL = {
id: 'mock-hazard-001',
status: 2,
statusName: '整改中',
reporterName: '张三',
reporterPhone: '13800138000',
reportDeptName: '安全管理部',
createdAt: '2026-06-01 09:30:00',
title: '消防通道堆放杂物',
level: 2,
levelName: '一般隐患',
source: '部门检查',
areaName: '生产车间A区',
areaColor: '#409EFF',
address: '办公楼3层东侧消防通道',
tagName: '消防安全',
description: LONG_DESCRIPTION,
legalBasis: '《中华人民共和国消防法》第二十八条',
regulationName: '《中华人民共和国消防法》第二十八条',
attachments: [
{
fileName: 'hazard-1.jpg',
filePath: 'https://picsum.photos/seed/hazard1/200/200',
fileType: 'image'
},
{
fileName: 'hazard-2.jpg',
filePath: 'https://picsum.photos/seed/hazard2/200/200',
fileType: 'image'
},
{
fileName: 'hazard-3.jpg',
filePath: 'https://picsum.photos/seed/hazard3/200/200',
fileType: 'image'
}
],
assignFlows: [
{
assignId: 'mock-assign-001',
assignerName: '李主管',
assignTime: '2026-06-02 10:00:00',
assigneeName: '王整改',
assignDeptName: '设备保障部',
assignRemark: '该隐患位于主疏散通道,请优先处理,务必在整改期限内完成闭环。',
priorityName: '高',
deadline: '2026-06-10 18:00:00',
rectify: {
rectifyId: 'mock-rectify-001',
rectifierName: '王整改',
rectifyTime: '2026-06-08 15:20:00',
rectifyStatusName: '已完成',
rectifyPlan:
'立即清理消防通道杂物,并设置警示标识,后续每周巡查一次。' +
'对责任区域进行划分,明确禁止堆放物料的范围,同时组织一次消防安全培训。',
rectifyResult: '已完成清理,通道恢复畅通,现场已张贴禁止堆放标识。',
rectificationMeasures: '组织人员清理杂物,划定禁止堆放区域,增加日常巡检频次。',
controlMeasures: '安装监控并纳入日常巡检清单,发现反弹情况立即上报。',
rectifierNames: ['王整改', '赵协助'],
managerNames: ['李主管'],
planCost: 500,
actualCost: 380,
attachments: [
{
fileName: 'rectify-1.jpg',
filePath: 'https://picsum.photos/seed/rectify1/200/200',
fileType: 'image'
},
{
fileName: 'rectify-2.jpg',
filePath: 'https://picsum.photos/seed/rectify2/200/200',
fileType: 'image'
}
],
signPath: 'https://picsum.photos/seed/sign1/300/120'
},
verifies: [
{
verifierName: '陈验收',
verifyTime: '2026-06-09 11:00:00',
verifyDeptName: '安全监察组',
resultName: '通过',
remark: '现场复查通道畅通,整改到位,未发现反弹迹象。',
attachments: [
{
fileName: 'verify-1.jpg',
filePath: 'https://picsum.photos/seed/verify1/200/200',
fileType: 'image'
},
{
fileName: 'verify-2.jpg',
filePath: 'https://picsum.photos/seed/verify2/200/200',
fileType: 'image'
}
],
signPath: 'https://picsum.photos/seed/sign2/300/120'
}
],
writeOffs: [
{
verifierName: '刘销号',
verifyTime: '2026-06-10 16:30:00',
writeoffDeptName: '公司安委会',
resultName: '通过',
remark: '隐患已消除,整改资料齐全,同意销号。',
attachments: [
{
fileName: 'writeoff-1.jpg',
filePath: 'https://picsum.photos/seed/writeoff1/200/200',
fileType: 'image'
}
],
signPath: 'https://picsum.photos/seed/sign3/300/120'
}
]
}
]
};

View File

@@ -0,0 +1 @@
export * from './hazardDetail.js';

View File

@@ -0,0 +1,164 @@
import { ref, watch, nextTick, getCurrentInstance } from 'vue';
import { onReady } from '@dcloudio/uni-app';
import { generateHazardHistory } from './hazardDetail.js';
/**
* 隐患详情左右联动滚动(与 HazardDetailPanel 保持一致)
* - 右侧滚动时,根据 node-section 位置同步左侧 activeIndex
* - 点击左侧步骤时,右侧 scroll-into-view 定位到对应卡片
*/
export function useHazardDetailScroll(detailSource, loadingSource) {
const instance = getCurrentInstance();
const queryScope = instance?.proxy || instance;
const historyList = ref([]);
const activeIndex = ref(0);
const sectionOffsets = ref([0]);
const contentViewHeight = ref(0);
const contentScrollIntoView = ref('');
const stepScrollIntoView = ref('');
const scrollWithAnimation = ref(true);
const isProgrammaticScroll = ref(false);
let measureLayoutTimer = null;
const measureLayout = () => {
if (!historyList.value.length) return;
nextTick(() => {
const query = uni.createSelectorQuery().in(queryScope);
query.select('.content-scroll').boundingClientRect();
query.select('.content-scroll').scrollOffset();
query.selectAll('.node-section').boundingClientRect();
query.exec((res) => {
const containerRect = res?.[0];
const scrollOffset = res?.[1];
const sections = res?.[2] || [];
if (!containerRect || !sections.length) return;
contentViewHeight.value = containerRect.height || 0;
const baseScrollTop = scrollOffset?.scrollTop || 0;
sectionOffsets.value = sections.map(
(section) => section.top - containerRect.top + baseScrollTop
);
});
});
};
const scheduleMeasureLayout = () => {
clearTimeout(measureLayoutTimer);
measureLayoutTimer = setTimeout(() => {
measureLayout();
}, 80);
};
const rebuildHistory = (data) => {
historyList.value = generateHazardHistory(data);
activeIndex.value = 0;
contentScrollIntoView.value = '';
scheduleMeasureLayout();
setTimeout(scheduleMeasureLayout, 300);
};
const syncActiveIndex = (scrollTop, scrollHeight = 0) => {
const count = historyList.value.length;
if (!count) return;
if (scrollHeight > 0 && contentViewHeight.value > 0) {
if (scrollTop + contentViewHeight.value >= scrollHeight - 60) {
if (activeIndex.value !== count - 1) {
activeIndex.value = count - 1;
}
return;
}
}
const offsets = sectionOffsets.value;
if (!offsets.length) return;
let idx = 0;
for (let i = offsets.length - 1; i >= 0; i--) {
if (scrollTop >= offsets[i] - 80) {
idx = i;
break;
}
}
if (idx !== activeIndex.value) {
activeIndex.value = idx;
}
};
const onContentScroll = (e) => {
if (isProgrammaticScroll.value) return;
const { scrollTop = 0, scrollHeight = 0 } = e.detail || {};
syncActiveIndex(scrollTop, scrollHeight);
scheduleMeasureLayout();
};
const onScrollToLower = () => {
const count = historyList.value.length;
if (count > 0 && activeIndex.value !== count - 1) {
activeIndex.value = count - 1;
}
};
const scrollToNode = (index) => {
if (index < 0 || index >= historyList.value.length) return;
isProgrammaticScroll.value = true;
scrollWithAnimation.value = true;
activeIndex.value = index;
contentScrollIntoView.value = 'hazard-node-' + index;
setTimeout(() => {
contentScrollIntoView.value = '';
isProgrammaticScroll.value = false;
scheduleMeasureLayout();
}, 350);
};
watch(
() => (typeof loadingSource === 'function' ? loadingSource() : loadingSource?.value),
(loading) => {
if (!loading) {
scheduleMeasureLayout();
setTimeout(scheduleMeasureLayout, 300);
}
}
);
watch(
() => (typeof detailSource === 'function' ? detailSource() : detailSource?.value),
(val) => rebuildHistory(val),
{ immediate: true, deep: true }
);
watch(activeIndex, (index) => {
stepScrollIntoView.value = 'hazard-step-' + index;
setTimeout(() => {
stepScrollIntoView.value = '';
}, 300);
});
watch(
() => historyList.value.length,
() => scheduleMeasureLayout()
);
onReady(() => {
scheduleMeasureLayout();
setTimeout(scheduleMeasureLayout, 300);
});
return {
historyList,
activeIndex,
contentScrollIntoView,
stepScrollIntoView,
scrollWithAnimation,
onContentScroll,
onScrollToLower,
scrollToNode,
scheduleMeasureLayout
};
}

View File

@@ -12,7 +12,7 @@
{ {
"path": "pages/index/index", "path": "pages/index/index",
"style": { "style": {
"navigationBarTitleText": "三查一曝光", "navigationBarTitleText": "湘西州“三个一”安全管理平台",
"navigationStyle": "custom", "navigationStyle": "custom",
"navigationBarTextStyle": "white" "navigationBarTextStyle": "white"
} }
@@ -35,6 +35,19 @@
"navigationBarTitleText": "检查结果" "navigationBarTitleText": "检查结果"
} }
}, },
{
"path": "pages/Inspectionresult/list",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "检查列表"
}
},
{
"path": "pages/Inspectionresult/detail",
"style": {
"navigationBarTitleText": "排查详情"
}
},
{ {
"path": "pages/membermanagemen/membermanagemen", "path": "pages/membermanagemen/membermanagemen",
"style": { "style": {
@@ -95,6 +108,15 @@
"navigationBarTitleText": "查看隐患" "navigationBarTitleText": "查看隐患"
} }
}, },
{
"path": "pages/hiddendanger/detail2",
"style": {
"navigationBarTitleText": "隐患详情",
"navigationStyle": "custom",
"navigationBarTextStyle": "white",
"disableScroll": true
}
},
{ {
"path":"pages/hiddendanger/rectification", "path":"pages/hiddendanger/rectification",
"style": { "style": {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,284 @@
<template>
<view class="page">
<u-navbar
title="检查列表"
:placeholder="true"
:safeAreaInsetTop="true"
bgColor="#3375e6"
titleColor="#ffffff"
leftIconColor="#ffffff"
:autoBack="false"
@leftClick="goHome"
/>
<view class="page-content padding">
<view v-if="loading" class="empty-tip text-gray text-center padding">加载中...</view>
<view v-else-if="!tableId" class="empty-tip text-gray text-center padding">
请从首页检查计划进入
</view>
<view v-else-if="checkPlanData.length === 0" class="empty-tip text-gray text-center padding">
暂无检查任务
</view>
<view
v-else
class="plan-card margin-bottom"
v-for="item in checkPlanData"
:key="item.id || item.taskDate"
>
<view class="plan-header">
<text class="plan-header-title">{{ item.name || planName || '检查任务' }}</text>
</view>
<view class="plan-body">
<view class="flex">
<view class="border-border margin-right-xs">{{ item.runModeName }}完成</view>
<view class="border-border">{{ item.cycle }}</view>
</view>
<view v-if="item.taskDate" class="flex text-gray margin-top">
<view>任务日期</view>
<view style="color: #333333;">{{ formatDate(item.taskDate) }}</view>
</view>
<view class="flex text-gray margin-top">
<view>计划时间</view>
<view style="color: #333333;">
{{ formatDate(item.planStartTime) }}{{ formatDate(item.planEndTime) }}
</view>
</view>
<view class="flex margin-top align-center">
<view style="color: #B5B5B5;">完成进度</view>
<view class="flex align-center margin-left-sm">
<view class="cu-progress round">
<view class="bg-green" :style="{ width: formatProgress(item.progress) + '%' }"></view>
</view>
<text class="margin-left-sm">{{ formatProgress(item.progress) }}%</text>
</view>
</view>
<view class="plan-stats margin-top">
<view class="plan-stat-item">
<view class="plan-stat-num text-orange">{{ item.totalCount ?? 0 }}</view>
<view class="plan-stat-label">总检项</view>
</view>
<view class="plan-stat-item">
<view class="plan-stat-num text-yellow">{{ item.pendingCount ?? 0 }}</view>
<view class="plan-stat-label">待排查</view>
</view>
<view class="plan-stat-item">
<view class="plan-stat-num text-red">{{ item.unusualNum ?? 0 }}</view>
<view class="plan-stat-label">异常数</view>
</view>
<view class="plan-stat-item">
<view class="plan-stat-num text-blue">{{ item.finishedCount ?? 0 }}</view>
<view class="plan-stat-label">已完成</view>
</view>
</view>
<view class="margin-top margin-bottom flex justify-end">
<!-- <button class="cu-btn round lg light bg-blue margin-right" @click.stop="viewDetails(item)">
查看详情
</button> -->
<button
class="cu-btn round lg bg-blue"
@click.stop="startCheck(item)"
>
检查处置
</button>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad, onShow, onBackPress } from '@dcloudio/uni-app';
import { getPlanTableDetail } from '@/request/api.js';
const loading = ref(true);
const checkPlanData = ref([]);
const tableId = ref('');
const planName = ref('');
const listParams = {
pageNum: 1,
pageSize: 100,
name: ''
};
const formatDate = (dateStr) => {
if (!dateStr) return '';
return String(dateStr).split(' ')[0];
};
const formatProgress = (progress) => {
const num = Number(progress);
if (Number.isNaN(num)) return 0;
return Math.min(100, Math.max(0, num));
};
const fetchList = async () => {
if (!tableId.value) {
loading.value = false;
checkPlanData.value = [];
return;
}
loading.value = true;
try {
const res = await getPlanTableDetail({
...listParams,
tableId: tableId.value
});
if (res.code === 0) {
checkPlanData.value = res.data?.records || [];
} else {
uni.showToast({ title: res.msg || '获取检查清单失败', icon: 'none' });
}
} catch (error) {
console.error('获取检查清单失败:', error);
uni.showToast({ title: '获取检查清单失败', icon: 'none' });
} finally {
loading.value = false;
}
};
const viewDetails = (item) => {
uni.navigateTo({
url: `/pages/plandetail/plandetail?id=${item.id || tableId.value}`
});
};
const startCheck = (item) => {
const oneTableId = item.id;
const taskDate = formatDate(item.taskDate);
if (!oneTableId || !taskDate) {
uni.showToast({ title: '缺少任务参数', icon: 'none' });
return;
}
const name = item.name || planName.value || '';
const tableIdQuery = tableId.value ? `&tableId=${tableId.value}` : '';
uni.navigateTo({
url: `/pages/Inspectionresult/detail?oneTableId=${oneTableId}&taskDate=${encodeURIComponent(taskDate)}&name=${encodeURIComponent(name)}${tableIdQuery}`
});
};
onLoad((options) => {
if (options.tableId) {
tableId.value = options.tableId;
}
if (options.name) {
planName.value = decodeURIComponent(options.name);
}
});
onShow(() => {
fetchList();
});
const goHome = () => {
uni.switchTab({
url: '/pages/index/index'
});
};
onBackPress(() => {
goHome();
return true;
});
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: #EBF2FC;
}
.empty-tip {
font-size: 28rpx;
}
.plan-card {
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0rpx 2rpx 6rpx 2rpx rgba(0, 0, 0, 0.06);
}
.plan-header {
background: linear-gradient(135deg, #4A90E2 0%, #2667E9 100%);
padding: 24rpx 30rpx;
display: flex;
align-items: center;
.plan-header-title {
color: #fff;
font-size: 30rpx;
font-weight: bold;
}
}
.plan-body {
padding: 24rpx 30rpx 10rpx 30rpx;
background: #fff;
}
.plan-stats {
display: flex;
background: #F5F7FA;
border-radius: 12rpx;
border: 1rpx solid #E8ECF0;
overflow: hidden;
.plan-stat-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
border-right: 1rpx solid #E8ECF0;
&:last-child {
border-right: none;
}
}
.plan-stat-num {
font-size: 36rpx;
font-weight: bold;
}
.plan-stat-label {
font-size: 24rpx;
color: #666;
margin-top: 8rpx;
}
}
.cu-progress {
width: 300rpx;
height: 20rpx;
background: #ebeef5;
border-radius: 100rpx;
overflow: hidden;
view {
height: 100%;
border-radius: 100rpx;
transition: width 0.3s ease;
}
}
.bg-green {
background: #2667E9;
}
.border-border {
padding: 10rpx;
background: #EEF3FF;
border-radius: 4rpx;
border: 2rpx solid #AAC5FC;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
font-size: 28rpx;
color: #2667E9;
}
</style>

View File

@@ -129,7 +129,7 @@
<view class="form-label"> <view class="form-label">
<text>开始时间</text> <text>开始时间</text>
</view> </view>
<view class="picker-input" @click="showStartDatePicker = true"> <view class="picker-input" @click="openStartDatePicker">
<text :class="formData.startDate ? 'picker-value' : 'picker-placeholder'"> <text :class="formData.startDate ? 'picker-value' : 'picker-placeholder'">
{{ formData.startDate || '请选择开始时间' }} {{ formData.startDate || '请选择开始时间' }}
</text> </text>
@@ -137,8 +137,9 @@
</view> </view>
<up-datetime-picker <up-datetime-picker
:show="showStartDatePicker" :show="showStartDatePicker"
mode="datetime" mode="date"
v-model="startDateValue" v-model="startDateValue"
:minDate="todayMinDate"
@confirm="onStartDateConfirm" @confirm="onStartDateConfirm"
@cancel="showStartDatePicker = false" @cancel="showStartDatePicker = false"
@close="showStartDatePicker = false" @close="showStartDatePicker = false"
@@ -150,7 +151,7 @@
<view class="form-label"> <view class="form-label">
<text>结束时间</text> <text>结束时间</text>
</view> </view>
<view class="picker-input" @click="showEndDatePicker = true"> <view class="picker-input" @click="openEndDatePicker">
<text :class="formData.endDate ? 'picker-value' : 'picker-placeholder'"> <text :class="formData.endDate ? 'picker-value' : 'picker-placeholder'">
{{ formData.endDate || '请选择结束时间' }} {{ formData.endDate || '请选择结束时间' }}
</text> </text>
@@ -158,8 +159,9 @@
</view> </view>
<up-datetime-picker <up-datetime-picker
:show="showEndDatePicker" :show="showEndDatePicker"
mode="datetime" mode="date"
v-model="endDateValue" v-model="endDateValue"
:minDate="endDateMinDate"
@confirm="onEndDateConfirm" @confirm="onEndDateConfirm"
@cancel="showEndDatePicker = false" @cancel="showEndDatePicker = false"
@close="showEndDatePicker = false" @close="showEndDatePicker = false"
@@ -426,8 +428,23 @@ const onSwitchChange = (e) => {
}; };
// 日期选择器值 // 日期选择器值
const startDateValue = ref(Number(new Date())); const getTodayTimestamp = () => {
const endDateValue = ref(Number(new Date())); const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
};
const todayMinDate = computed(() => getTodayTimestamp());
const endDateMinDate = computed(() => {
if (formData.startDate) {
const [year, month, day] = formData.startDate.split('-').map(Number);
return new Date(year, month - 1, day).getTime();
}
return getTodayTimestamp();
});
const startDateValue = ref(getTodayTimestamp());
const endDateValue = ref(getTodayTimestamp());
// 选择器显示控制 // 选择器显示控制
const showDeptPicker = ref(false); const showDeptPicker = ref(false);
@@ -619,22 +636,46 @@ const onCycleConfirm = (e) => {
showCyclePicker.value = false; showCyclePicker.value = false;
}; };
// 日期时间格式化(精确到时分秒 // 日期格式化(年月日
const formatDateTime = (timestamp) => { const formatDate = (timestamp) => {
const date = new Date(timestamp); const date = new Date(timestamp);
const year = date.getFullYear(); const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0'); return `${year}-${month}-${day}`;
const minutes = String(date.getMinutes()).padStart(2, '0'); };
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; const parseDateValue = (dateStr) => {
if (!dateStr) return 0;
const [datePart] = dateStr.split(' ');
const [year, month, day] = datePart.split('-').map(Number);
return new Date(year, month - 1, day).getTime();
};
const openStartDatePicker = () => {
const today = getTodayTimestamp();
if (startDateValue.value < today) {
startDateValue.value = today;
}
showStartDatePicker.value = true;
};
const openEndDatePicker = () => {
const minDate = endDateMinDate.value;
if (endDateValue.value < minDate) {
endDateValue.value = minDate;
}
showEndDatePicker.value = true;
}; };
const onStartDateConfirm = (e) => { const onStartDateConfirm = (e) => {
const selectedDate = formatDateTime(e.value); const selectedDate = formatDate(e.value);
// 如果已经选了结束时间,校验开始时间不能晚于结束时间 const todayStr = formatDate(getTodayTimestamp());
if (formData.endDate && new Date(selectedDate) > new Date(formData.endDate)) { if (selectedDate < todayStr) {
uni.showToast({ title: '开始时间不能早于今天', icon: 'none' });
return;
}
if (formData.endDate && parseDateValue(selectedDate) > parseDateValue(formData.endDate)) {
uni.showToast({ title: '开始时间不能晚于结束时间', icon: 'none' }); uni.showToast({ title: '开始时间不能晚于结束时间', icon: 'none' });
return; return;
} }
@@ -643,9 +684,8 @@ const onStartDateConfirm = (e) => {
}; };
const onEndDateConfirm = (e) => { const onEndDateConfirm = (e) => {
const selectedDate = formatDateTime(e.value); const selectedDate = formatDate(e.value);
// 如果已经选了开始时间,校验结束时间不能早于开始时间 if (formData.startDate && parseDateValue(selectedDate) < parseDateValue(formData.startDate)) {
if (formData.startDate && new Date(selectedDate) < new Date(formData.startDate)) {
uni.showToast({ title: '结束时间不能早于开始时间', icon: 'none' }); uni.showToast({ title: '结束时间不能早于开始时间', icon: 'none' });
return; return;
} }
@@ -1101,7 +1141,7 @@ const handleSave = async () => {
uni.showToast({ title: '请选择计划时间', icon: 'none' }); uni.showToast({ title: '请选择计划时间', icon: 'none' });
return; return;
} }
if (new Date(formData.endDate) < new Date(formData.startDate)) { if (parseDateValue(formData.endDate) < parseDateValue(formData.startDate)) {
uni.showToast({ title: '结束时间不能早于开始时间', icon: 'none' }); uni.showToast({ title: '结束时间不能早于开始时间', icon: 'none' });
return; return;
} }
@@ -1157,8 +1197,8 @@ const handleSave = async () => {
itemIds: itemIds, // 从检查库选择的库id数组 itemIds: itemIds, // 从检查库选择的库id数组
cycle: cycleMap[formData.cycleName] || 1, cycle: cycleMap[formData.cycleName] || 1,
isWeekend: workdaySwitch.value ? 1 : 2, isWeekend: workdaySwitch.value ? 1 : 2,
planStartTime: formData.startDate, planStartTime: `${formData.startDate} 00:00:00`,
planEndTime: formData.endDate planEndTime: `${formData.endDate} 23:59:59`
}; };
// 如果是指定人员模式添加执行人员id // 如果是指定人员模式添加执行人员id

File diff suppressed because it is too large Load Diff

View File

@@ -74,7 +74,14 @@
</view> </view>
<view class="signature-box margin-bottom"> <view class="signature-box margin-bottom">
<view v-if="!showCanvas" class="signature-display flex align-center justify-center" style="width: 100%; height: 160px; background-color: #f8f8f8; display: flex; align-items: center; justify-content: center;"> <view v-if="!showCanvas" class="signature-display flex align-center justify-center" style="width: 100%; height: 160px; background-color: #f8f8f8; display: flex; align-items: center; justify-content: center;">
<image :src="signatureUrl" class="signature-img" mode="aspectFit" style="width: 100%; height: 100%;"></image> <image
v-if="signatureUrl"
:src="signatureUrl"
class="signature-img"
mode="aspectFit"
style="width: 100%; height: 100%;"
></image>
<text v-else class="signature-placeholder">暂无签名请点击重新签名</text>
</view> </view>
<!-- 改为 v-if 解决小程序原生 canvas 真机渲染与生命周期挂载残留问题 --> <!-- 改为 v-if 解决小程序原生 canvas 真机渲染与生命周期挂载残留问题 -->
<view v-if="showCanvas" class="signature-pad-wrap" style="border: 1px dashed #dcdfe6; border-radius: 8rpx; overflow: hidden; background-color: #f8f8f8;"> <view v-if="showCanvas" class="signature-pad-wrap" style="border: 1px dashed #dcdfe6; border-radius: 8rpx; overflow: hidden; background-color: #f8f8f8;">
@@ -87,9 +94,10 @@
:lineWidth="3" :lineWidth="3"
:enableHistory="false" :enableHistory="false"
@confirm="(res) => onSignatureConfirm(res.tempFilePath)" @confirm="(res) => onSignatureConfirm(res.tempFilePath)"
@start="isSignatureEmpty = false" @start="onSignatureStart"
@signing="isSignatureEmpty = false" @signing="onSignatureSigning"
@clear="isSignatureEmpty = true" @end="onSignatureEnd"
@clear="onSignatureClear"
> >
<template #footer></template> <template #footer></template>
</wd-signature> </wd-signature>
@@ -105,14 +113,17 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, watch, nextTick, getCurrentInstance } from 'vue'; import { ref, reactive, nextTick, getCurrentInstance } from 'vue';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad, onHide } from '@dcloudio/uni-app';
import { acceptanceRectification, getHiddenDangerDetail } from '@/request/api.js'; import { acceptanceRectification, getHiddenDangerDetail } from '@/request/api.js';
import { toImageUrl } from '@/request/request.js'; import { toImageUrl } from '@/request/request.js';
import { buildDraftKey, buildDraftKeyCompact, DRAFT_NS } from '@/utils/draftCache.js';
import { useDraftCache } from '@/utils/useDraftCache.js';
import { import {
createUploadListHandlers, createUploadListHandlers,
buildAttachmentItem, buildAttachmentItem,
uploadToCloud uploadToCloud,
toSubmitFileUrl
} from '@/utils/upload.js'; } from '@/utils/upload.js';
// 页面参数 // 页面参数
@@ -166,14 +177,133 @@
const signatureRef = ref(null); // 签名组件 ref const signatureRef = ref(null); // 签名组件 ref
const isSignatureEmpty = ref(true); // 签名是否为空 const isSignatureEmpty = ref(true); // 签名是否为空
const isSubmitting = ref(false); // 是否正在提交表单 const isSubmitting = ref(false); // 是否正在提交表单
const hasDraft = ref(false);
const showRestoreBanner = ref(false); // 独立控制提示 Banner
const isRestoring = ref(false); // 正在恢复标志
const isInitialized = ref(false); // 初始化标识
const signaturePaths = ref([]); // 缓存手写签名的绘制路径 const signaturePaths = ref([]); // 缓存手写签名的绘制路径
const signatureLocalPath = ref(''); // 未上传云端的本地签名临时图
const isDraftExporting = ref(false); // 是否为草稿导出(非提交上传)
let signatureExportTimer = null;
const getDraftKey = () => `draft_accept_${rectifyId.value || ''}`; /** 将已上传的签名地址应用到预览区 */
const applySignatureFromServer = (signPath) => {
const url = signPath ? toSubmitFileUrl(signPath) : '';
if (!url) {
showCanvas.value = true;
signatureServerPath.value = '';
signatureUrl.value = '';
signatureLocalPath.value = '';
isSignatureEmpty.value = true;
return;
}
signatureServerPath.value = url;
signatureUrl.value = url;
signatureLocalPath.value = '';
showCanvas.value = false;
isSignatureEmpty.value = false;
};
/** 将本地临时签名图应用到预览区(草稿回显) */
const applySignatureFromLocal = (localPath) => {
if (!localPath) return;
signatureLocalPath.value = localPath;
signatureUrl.value = localPath;
signatureServerPath.value = '';
showCanvas.value = false;
isSignatureEmpty.value = false;
};
const acceptDraftHasContent = (data) => {
const form = data.formData || {};
return !!(
form.verifyRemark ||
(data.fileList1 && data.fileList1.length > 0) ||
data.signatureServerPath ||
data.signatureLocalPath ||
(data.signaturePaths && data.signaturePaths.length > 0)
);
};
const {
showRestoreBanner,
clear: clearDraft,
restore: restoreDraft,
save: saveDraft,
bindAutoSave
} = useDraftCache({
getKey: () => buildDraftKey(DRAFT_NS.ACCEPT, rectifyId.value),
getFallbackKeys: () => {
const primary = buildDraftKey(DRAFT_NS.ACCEPT, rectifyId.value);
const compact = buildDraftKeyCompact(DRAFT_NS.ACCEPT, rectifyId.value);
return compact !== primary ? [compact] : [];
},
getPayload: () => ({
formData: {
result: formData.result,
verifyRemark: formData.verifyRemark
},
fileList1: fileList1.value,
signatureServerPath: signatureServerPath.value,
signatureUrl: signatureUrl.value,
signatureLocalPath: signatureLocalPath.value,
showCanvas: showCanvas.value,
signaturePaths: signaturePaths.value
}),
hasContent: acceptDraftHasContent,
applyPayload: (data) => {
const form = data.formData || {};
formData.result = form.result !== undefined ? form.result : 1;
formData.verifyRemark = form.verifyRemark || '';
fileList1.value = data.fileList1 || [];
signaturePaths.value = data.signaturePaths || [];
if (data.signatureServerPath || data.signatureUrl) {
applySignatureFromServer(data.signatureServerPath || data.signatureUrl);
} else if (data.signatureLocalPath) {
applySignatureFromLocal(data.signatureLocalPath);
} else {
showCanvas.value = data.showCanvas !== undefined ? data.showCanvas : true;
signatureServerPath.value = '';
signatureUrl.value = '';
signatureLocalPath.value = '';
isSignatureEmpty.value = true;
}
},
clearForm: () => {
formData.result = 1;
formData.verifyRemark = '';
fileList1.value = [];
signatureServerPath.value = '';
signatureUrl.value = '';
signatureLocalPath.value = '';
showCanvas.value = true;
signaturePaths.value = [];
if (signatureRef.value) {
signatureRef.value.clear();
}
},
canSave: () => !!rectifyId.value,
requireInitialized: true,
onAfterRestore: (data) => {
if (data.signatureServerPath || data.signatureUrl || data.signatureLocalPath) {
return;
}
if (data.signaturePaths?.length > 0) {
setTimeout(() => {
if (signatureRef.value) {
isSignatureEmpty.value = false;
}
}, 450);
}
}
});
bindAutoSave(() => [
formData.result,
formData.verifyRemark,
fileList1.value,
signatureServerPath.value,
signatureUrl.value,
signatureLocalPath.value,
showCanvas.value,
signaturePaths.value
]);
// 获取完整图片路径 // 获取完整图片路径
const getFullPath = (filePath) => { const getFullPath = (filePath) => {
@@ -279,8 +409,8 @@
// 触发组件导出,导出成功会回调 onSignatureConfirm // 触发组件导出,导出成功会回调 onSignatureConfirm
signatureRef.value.confirm(); signatureRef.value.confirm();
} else { } else {
// 已经有回显的签名 // 已经有回显的签名(云端或本地草稿)
if (!signatureServerPath.value) { if (!signatureServerPath.value && !signatureLocalPath.value) {
uni.showToast({ uni.showToast({
title: '请进行电子签名', title: '请进行电子签名',
icon: 'none' icon: 'none'
@@ -289,7 +419,18 @@
} }
isSubmitting.value = true; isSubmitting.value = true;
uni.showLoading({ title: '正在提交...', mask: true }); uni.showLoading({ title: '正在提交...', mask: true });
try {
if (!signatureServerPath.value && signatureLocalPath.value) {
const { url } = await uploadToCloud(signatureLocalPath.value);
applySignatureFromServer(url);
}
await executeSubmit(); await executeSubmit();
} catch (err) {
isSubmitting.value = false;
uni.hideLoading();
console.error('签名上传失败:', err);
uni.showToast({ title: '签名上传失败,请重试', icon: 'none' });
}
} }
}; };
@@ -350,148 +491,47 @@
} }
}); });
// 电子签名画布手写线条变动回调 // 电子签名:手写过程中自动导出本地预览图写入草稿
const onSignatureChange = () => { const onSignatureStart = () => {
isSignatureEmpty.value = false; isSignatureEmpty.value = false;
signaturePaths.value = [];
if (rectifyId.value) {
saveDraft();
}
}; };
// 保存草稿 const onSignatureSigning = () => {
const saveDraft = () => { isSignatureEmpty.value = false;
if (isRestoring.value || !isInitialized.value) return; };
const key = getDraftKey();
const hasContent = formData.verifyRemark || const onSignatureClear = () => {
fileList1.value.length > 0 || isSignatureEmpty.value = true;
signatureServerPath.value || signatureLocalPath.value = '';
signaturePaths.value.length > 0; };
if (!hasContent) {
uni.removeStorageSync(key); const scheduleSignatureDraftExport = () => {
hasDraft.value = false; clearTimeout(signatureExportTimer);
signatureExportTimer = setTimeout(() => {
if (
!showCanvas.value ||
isSignatureEmpty.value ||
!signatureRef.value ||
isSubmitting.value ||
isDraftExporting.value
) {
return; return;
} }
isDraftExporting.value = true;
const data = { signatureRef.value.confirm();
formData: { }, 600);
result: formData.result,
verifyRemark: formData.verifyRemark
},
fileList1: fileList1.value,
signatureServerPath: signatureServerPath.value,
signatureUrl: signatureUrl.value,
showCanvas: showCanvas.value,
signaturePaths: signaturePaths.value
};
uni.setStorageSync(key, JSON.stringify(data));
hasDraft.value = true;
}; };
// 清空草稿 const onSignatureEnd = () => {
const clearDraft = (showToast = true) => {
const key = getDraftKey();
uni.removeStorageSync(key);
hasDraft.value = false;
showRestoreBanner.value = false;
isRestoring.value = true;
formData.result = 1;
formData.verifyRemark = '';
fileList1.value = [];
signatureServerPath.value = '';
signatureUrl.value = '';
showCanvas.value = true;
signaturePaths.value = [];
if (signatureRef.value) {
signatureRef.value.clear();
}
nextTick(() => {
isRestoring.value = false;
});
if (showToast) {
uni.showToast({ title: '草稿已清空', icon: 'none' });
}
};
// 恢复草稿
const restoreDraft = () => {
const key = getDraftKey();
const cached = uni.getStorageSync(key);
if (cached) {
try {
const data = JSON.parse(cached);
const hasContent = data.formData.verifyRemark ||
(data.fileList1 && data.fileList1.length > 0) ||
data.signatureServerPath ||
(data.signaturePaths && data.signaturePaths.length > 0);
if (!hasContent) {
isInitialized.value = true;
return;
}
isRestoring.value = true;
formData.result = data.formData.result !== undefined ? data.formData.result : 1;
formData.verifyRemark = data.formData.verifyRemark || '';
fileList1.value = data.fileList1 || [];
signatureServerPath.value = data.signatureServerPath || '';
signatureUrl.value = data.signatureUrl || '';
showCanvas.value = data.showCanvas !== undefined ? data.showCanvas : true;
signaturePaths.value = data.signaturePaths || [];
hasDraft.value = true;
showRestoreBanner.value = true;
// 延迟恢复签名画布线条重绘
if (signaturePaths.value.length > 0) {
setTimeout(() => {
if (signatureRef.value) {
isSignatureEmpty.value = false; isSignatureEmpty.value = false;
// wot-design-uni auto rendering scheduleSignatureDraftExport();
}
}, 450);
}
nextTick(() => {
isRestoring.value = false;
isInitialized.value = true;
});
uni.showToast({
title: '已自动恢复您上次未提交的内容',
icon: 'none',
duration: 2500
});
} catch (e) {
console.error('解析草稿失败:', e);
isRestoring.value = false;
isInitialized.value = true;
}
} else {
isInitialized.value = true;
}
}; };
// 深度监听验收项和签名笔画变化,自动保存草稿
watch(
() => [
formData.result,
formData.verifyRemark,
fileList1.value,
signatureServerPath.value,
signaturePaths.value
],
() => {
if (rectifyId.value) {
saveDraft();
}
},
{ deep: true }
);
// 清除画布 // 清除画布
const clearSignature = () => { const clearSignature = () => {
clearTimeout(signatureExportTimer);
isSignatureEmpty.value = true; isSignatureEmpty.value = true;
signatureLocalPath.value = '';
if (signatureRef.value) { if (signatureRef.value) {
signatureRef.value.clear(); signatureRef.value.clear();
} }
@@ -499,10 +539,12 @@
// 重新签字 // 重新签字
const reSign = () => { const reSign = () => {
clearTimeout(signatureExportTimer);
isSignatureEmpty.value = true; isSignatureEmpty.value = true;
showCanvas.value = true; showCanvas.value = true;
signatureUrl.value = ''; signatureUrl.value = '';
signatureServerPath.value = ''; signatureServerPath.value = '';
signatureLocalPath.value = '';
nextTick(() => { nextTick(() => {
if (signatureRef.value) { if (signatureRef.value) {
signatureRef.value.clear(); signatureRef.value.clear();
@@ -520,12 +562,17 @@
// 签名导出成功回调 // 签名导出成功回调
const onSignatureConfirm = async (tempFilePath) => { const onSignatureConfirm = async (tempFilePath) => {
if (isDraftExporting.value) {
isDraftExporting.value = false;
applySignatureFromLocal(tempFilePath);
saveDraft();
return;
}
try { try {
const { url } = await uploadToCloud(tempFilePath); const { url } = await uploadToCloud(tempFilePath);
signatureServerPath.value = url; applySignatureFromServer(url);
signatureUrl.value = url; saveDraft();
showCanvas.value = false;
isSignatureEmpty.value = false;
if (isSubmitting.value) { if (isSubmitting.value) {
await executeSubmit(); await executeSubmit();
@@ -538,6 +585,14 @@
} }
}; };
onHide(() => {
if (showCanvas.value && !isSignatureEmpty.value && signatureRef.value && !isDraftExporting.value) {
isDraftExporting.value = true;
signatureRef.value.confirm();
}
saveDraft();
});
onLoad((options) => { onLoad((options) => {
// 计算签名画布宽度 // 计算签名画布宽度
try { try {

View File

@@ -91,9 +91,11 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, watch, nextTick } from 'vue'; import { ref, computed } from 'vue';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { getDepartmentPersonUsers,assignHiddenDanger } from '@/request/api.js'; import { getDepartmentPersonUsers,assignHiddenDanger } from '@/request/api.js';
import { buildDraftKey, DRAFT_NS } from '@/utils/draftCache.js';
import { useDraftCache } from '@/utils/useDraftCache.js';
// 页面参数 // 页面参数
const hazardId = ref(''); const hazardId = ref('');
@@ -163,6 +165,32 @@
const dateValue = ref(Date.now()); const dateValue = ref(Date.now());
const selectedDate = ref(''); const selectedDate = ref('');
// 草稿(仅缓存整改期限,不缓存人员选择)
const {
showRestoreBanner,
clear: clearDraft,
restore: restoreDraft,
bindAutoSave
} = useDraftCache({
getKey: () => buildDraftKey(DRAFT_NS.ASSIGN, hazardId.value),
getPayload: () => ({
selectedDate: selectedDate.value,
dateValue: dateValue.value
}),
hasContent: (payload) => !!payload.selectedDate,
applyPayload: (payload) => {
selectedDate.value = payload.selectedDate || '';
dateValue.value = payload.dateValue || Date.now();
},
clearForm: () => {
selectedDate.value = '';
dateValue.value = Date.now();
},
canSave: () => !!hazardId.value
});
bindAutoSave(() => [selectedDate.value]);
// 获取部门人员列表 // 获取部门人员列表
const fetchDeptUsers = async () => { const fetchDeptUsers = async () => {
try { try {
@@ -233,92 +261,6 @@
} }
}; };
// 草稿缓存与恢复逻辑 (移至底部以确保 formData 等响应式状态已被正常定义)
const hasDraft = ref(false);
const showRestoreBanner = ref(false); // 独立控制提示 Banner仅在初次确实从本地恢复了内容时才显示
const isRestoring = ref(false); // 正在恢复标志避免触发冗余watch
const getDraftKey = () => `draft_assign_${hazardId.value || ''}`;
// 保存草稿 (排除选择器人员缓存,仅缓存整改期限日期值)
const saveDraft = () => {
if (isRestoring.value) return;
const key = getDraftKey();
const hasContent = selectedDate.value;
if (!hasContent) {
uni.removeStorageSync(key);
hasDraft.value = false;
return;
}
const data = {
selectedDate: selectedDate.value,
dateValue: dateValue.value
};
uni.setStorageSync(key, JSON.stringify(data));
hasDraft.value = true;
};
// 清空草稿
const clearDraft = (showToast = true) => {
const key = getDraftKey();
uni.removeStorageSync(key);
hasDraft.value = false;
showRestoreBanner.value = false;
isRestoring.value = true;
selectedDate.value = '';
dateValue.value = Date.now();
nextTick(() => {
isRestoring.value = false;
});
if (showToast) {
uni.showToast({ title: '草稿已清空', icon: 'none' });
}
};
// 恢复草稿
const restoreDraft = () => {
const key = getDraftKey();
const cached = uni.getStorageSync(key);
if (cached) {
try {
const data = JSON.parse(cached);
const hasContent = data.selectedDate;
if (!hasContent) return;
isRestoring.value = true;
selectedDate.value = data.selectedDate || '';
dateValue.value = data.dateValue || Date.now();
hasDraft.value = true;
showRestoreBanner.value = true; // 确实存在内容并恢复了,才亮起提示 Banner
nextTick(() => {
isRestoring.value = false;
});
uni.showToast({
title: '已自动恢复您上次未提交的内容',
icon: 'none',
duration: 2500
});
} catch (e) {
console.error('解析草稿失败:', e);
isRestoring.value = false;
}
}
};
// 监听变量变化,自动保存草稿
watch(
() => [selectedDate.value],
() => {
if (hazardId.value) {
saveDraft();
}
}
);
onLoad((options) => { onLoad((options) => {
if (options.hazardId) hazardId.value = options.hazardId; if (options.hazardId) hazardId.value = options.hazardId;
if (options.assignId) assignId.value = options.assignId; if (options.assignId) assignId.value = options.assignId;

View File

@@ -0,0 +1,243 @@
<template>
<view class="page">
<view class="top-gradient-wrap">
<u-navbar
title="查看隐患"
:placeholder="true"
:safeAreaInsetTop="true"
bgColor="transparent"
titleColor="#ffffff"
leftIconColor="#ffffff"
:autoBack="true"
:border="false"
/>
<view class="summary-card">
<view class="summary-side summary-side--left">
<image class="summary-icon" src="/static/yinhuan_detail/status.png" mode="aspectFit" />
<view class="summary-icon-gap">
<view class="summary-text">
<view class="summary-label">隐患状态</view>
<view class="summary-status">{{ detailData.statusName || '-' }}</view>
</view>
</view>
</view>
<view class="summary-divider"></view>
<view class="summary-side summary-side--right">
<image class="summary-icon" src="/static/yinhuan_detail/date.png" mode="aspectFit" />
<view class="summary-text">
<view class="summary-label">提交日期</view>
<view class="summary-date">{{ detailData.createdAt || '-' }}</view>
</view>
</view>
</view>
</view>
<view class="panel-wrap">
<HazardDetailPanelV2
:detail="detailData"
:loading="loading"
:body-height="panelBodyHeight"
/>
</view>
</view>
</template>
<script setup>
import { ref, watch, nextTick, getCurrentInstance } from 'vue';
import { onLoad, onReady } from '@dcloudio/uni-app';
import HazardDetailPanelV2 from '@/components/hazardDetail/HazardDetailPanelV2.vue';
import { getHazardDetail } from '@/request/api.js';
const instance = getCurrentInstance();
const queryScope = instance?.proxy || instance;
const detailData = ref({});
const loading = ref(false);
const panelBodyHeight = ref(0);
const calcPanelBodyHeight = () => {
nextTick(() => {
const query = uni.createSelectorQuery().in(queryScope);
query.select('.panel-wrap').boundingClientRect();
query.exec((res) => {
const rect = res?.[0];
if (rect?.height > 0) {
panelBodyHeight.value = Math.floor(rect.height);
return;
}
const sys = uni.getSystemInfoSync();
panelBodyHeight.value = Math.floor(sys.windowHeight * 0.55);
});
});
};
const loadDetail = async (hazardId) => {
loading.value = true;
try {
const res = await getHazardDetail(hazardId);
if (res.code === 0 && res.data) {
detailData.value = res.data;
} else {
uni.showToast({ title: res.msg || '获取详情失败', icon: 'none' });
}
} catch (error) {
console.error('获取隐患详情失败:', error);
uni.showToast({ title: '获取详情失败', icon: 'none' });
} finally {
loading.value = false;
calcPanelBodyHeight();
setTimeout(calcPanelBodyHeight, 100);
setTimeout(calcPanelBodyHeight, 400);
}
};
onReady(() => {
calcPanelBodyHeight();
setTimeout(calcPanelBodyHeight, 100);
setTimeout(calcPanelBodyHeight, 400);
});
watch(loading, (isLoading) => {
if (!isLoading) {
calcPanelBodyHeight();
setTimeout(calcPanelBodyHeight, 100);
setTimeout(calcPanelBodyHeight, 400);
}
});
onLoad((options) => {
if (options.hazardId) {
loadDetail(options.hazardId);
return;
}
uni.showToast({ title: '缺少隐患ID', icon: 'none' });
setTimeout(() => {
uni.navigateBack();
}, 1500);
});
</script>
<style lang="scss" scoped>
.page {
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
box-sizing: border-box;
background: #f5f7fa;
}
/* 渐变只覆盖导航栏 + 顶部卡片区域,在卡片下沿开始淡出 */
.top-gradient-wrap {
flex-shrink: 0;
background: linear-gradient( 180deg, #046CEA 0%, #2158C8 28.44%, rgba(4,107,234,0) 100%);}
.summary-card {
margin: 32rpx 30rpx 0;
padding: 28rpx 30rpx 32rpx;
background: #fff;
border-radius: 20rpx;
display: flex;
align-items: flex-start;
box-sizing: border-box;
}
.summary-side {
display: flex;
align-items: flex-start;
}
.summary-side--left {
flex-shrink: 0;
align-items: center;
}
.summary-side--left .summary-text {
padding-top: 0;
padding-bottom: 0;
}
.summary-side--right {
flex: 1;
min-width: 0;
align-items: center;
}
.summary-side--right .summary-icon {
margin-right: 22rpx;
}
.summary-side--right .summary-text {
flex: 1;
min-width: 0;
padding-top: 0;
padding-bottom: 0;
}
/* 图标右边缘到竖线 = 169rpx文字靠左右侧留白 */
.summary-icon-gap {
width: 149rpx;
flex-shrink: 0;
box-sizing: border-box;
}
.summary-icon {
width: 55rpx;
height: 65rpx;
flex-shrink: 0;
}
.summary-side--left .summary-icon {
margin-right: 22rpx;
}
.summary-text {
flex-shrink: 0;
box-sizing: border-box;
}
.summary-label {
font-size: 24rpx;
color: #8f9ca2;
line-height: 34rpx;
}
.summary-status {
margin-top: 8rpx;
font-size: 28rpx;
font-weight: 400;
color: #333333;
line-height: 40rpx;
}
.summary-date {
margin-top: 8rpx;
font-size: 28rpx;
font-weight: 400;
color: #333333;
line-height: 40rpx;
white-space: nowrap;
}
.summary-divider {
width: 2rpx;
height: 72rpx;
flex-shrink: 0;
background: #eee;
margin-right: 40rpx;
}
.panel-wrap {
flex: 1;
min-height: 0;
height: 0;
margin-top: 12rpx;
padding: 0 30rpx 30rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
</style>

View File

@@ -235,9 +235,10 @@
:lineWidth="3" :lineWidth="3"
:enableHistory="false" :enableHistory="false"
@confirm="(res) => onSignatureConfirm(res.tempFilePath)" @confirm="(res) => onSignatureConfirm(res.tempFilePath)"
@start="isSignatureEmpty = false" @start="onSignatureStart"
@signing="isSignatureEmpty = false" @signing="onSignatureSigning"
@clear="isSignatureEmpty = true" @end="onSignatureEnd"
@clear="onSignatureClear"
> >
<template #footer></template> <template #footer></template>
</wd-signature> </wd-signature>
@@ -250,9 +251,11 @@
</template> </template>
<script setup> <script setup>
import {ref,reactive,computed,nextTick,watch,getCurrentInstance} from 'vue' import {ref,reactive,computed,nextTick,getCurrentInstance} from 'vue'
import {onLoad} from '@dcloudio/uni-app' import {onLoad, onHide} from '@dcloudio/uni-app'
import {submitRectification,getDepartmentPersonUsers,getRectifyDetail,getDeptUsersWithSubordinates,getHiddenDangerDetail,generateRectifyPlan} from '@/request/api.js' import {submitRectification,getDepartmentPersonUsers,getRectifyDetail,getDeptUsersWithSubordinates,getHiddenDangerDetail,generateRectifyPlan} from '@/request/api.js'
import { buildDraftKey, buildDraftKeyCompact, DRAFT_NS } from '@/utils/draftCache.js';
import { useDraftCache } from '@/utils/useDraftCache.js';
import { import {
createUploadListHandlers, createUploadListHandlers,
buildAttachmentItem, buildAttachmentItem,
@@ -279,10 +282,15 @@
const signatureRef = ref(null); // 签名组件 ref const signatureRef = ref(null); // 签名组件 ref
const isSignatureEmpty = ref(true); // 签名是否为空 const isSignatureEmpty = ref(true); // 签名是否为空
const isSubmitting = ref(false); // 是否正在提交表单 const isSubmitting = ref(false); // 是否正在提交表单
const signatureLocalPath = ref(''); // 未上传云端的本地签名临时图
const isDraftExporting = ref(false); // 是否为草稿导出(非提交上传)
let signatureExportTimer = null;
// 清除画布 // 清除画布
const clearSignature = () => { const clearSignature = () => {
clearTimeout(signatureExportTimer);
isSignatureEmpty.value = true; isSignatureEmpty.value = true;
signatureLocalPath.value = '';
if (signatureRef.value) { if (signatureRef.value) {
signatureRef.value.clear(); signatureRef.value.clear();
} }
@@ -308,15 +316,62 @@
showCanvas.value = true; showCanvas.value = true;
signatureServerPath.value = ''; signatureServerPath.value = '';
signatureUrl.value = ''; signatureUrl.value = '';
signatureLocalPath.value = '';
isSignatureEmpty.value = true; isSignatureEmpty.value = true;
return; return;
} }
signatureServerPath.value = url; signatureServerPath.value = url;
signatureUrl.value = url; signatureUrl.value = url;
signatureLocalPath.value = '';
showCanvas.value = false; showCanvas.value = false;
isSignatureEmpty.value = false; isSignatureEmpty.value = false;
}; };
/** 将本地临时签名图应用到预览区(草稿回显) */
const applySignatureFromLocal = (localPath) => {
if (!localPath) return;
signatureLocalPath.value = localPath;
signatureUrl.value = localPath;
signatureServerPath.value = '';
showCanvas.value = false;
isSignatureEmpty.value = false;
};
const onSignatureStart = () => {
isSignatureEmpty.value = false;
};
const onSignatureSigning = () => {
isSignatureEmpty.value = false;
};
const onSignatureClear = () => {
isSignatureEmpty.value = true;
signatureLocalPath.value = '';
};
const scheduleSignatureDraftExport = () => {
clearTimeout(signatureExportTimer);
signatureExportTimer = setTimeout(() => {
if (
!showCanvas.value ||
isSignatureEmpty.value ||
!signatureRef.value ||
isSubmitting.value ||
isDraftExporting.value
) {
return;
}
isDraftExporting.value = true;
signatureRef.value.confirm();
}, 600);
};
const onSignatureEnd = () => {
isSignatureEmpty.value = false;
scheduleSignatureDraftExport();
};
const onSignatureImageError = () => { const onSignatureImageError = () => {
console.error('签名图片加载失败:', signatureUrl.value); console.error('签名图片加载失败:', signatureUrl.value);
uni.showToast({ title: '签名图片加载失败', icon: 'none' }); uni.showToast({ title: '签名图片加载失败', icon: 'none' });
@@ -324,10 +379,12 @@
// 重新签字 // 重新签字
const reSign = () => { const reSign = () => {
clearTimeout(signatureExportTimer);
isSignatureEmpty.value = true; isSignatureEmpty.value = true;
showCanvas.value = true; showCanvas.value = true;
signatureUrl.value = ''; signatureUrl.value = '';
signatureServerPath.value = ''; signatureServerPath.value = '';
signatureLocalPath.value = '';
nextTick(() => { nextTick(() => {
if (signatureRef.value) { if (signatureRef.value) {
signatureRef.value.clear(); signatureRef.value.clear();
@@ -730,9 +787,17 @@
// 签名导出成功回调 // 签名导出成功回调
const onSignatureConfirm = async (tempFilePath) => { const onSignatureConfirm = async (tempFilePath) => {
if (isDraftExporting.value) {
isDraftExporting.value = false;
applySignatureFromLocal(tempFilePath);
saveDraft();
return;
}
try { try {
const { url } = await uploadToCloud(tempFilePath); const { url } = await uploadToCloud(tempFilePath);
applySignatureFromServer(url); applySignatureFromServer(url);
saveDraft();
if (isSubmitting.value) { if (isSubmitting.value) {
await executeSubmit(); await executeSubmit();
@@ -812,8 +877,8 @@
// 触发组件导出,导出成功会回调 onSignatureConfirm // 触发组件导出,导出成功会回调 onSignatureConfirm
signatureRef.value.confirm(); signatureRef.value.confirm();
} else { } else {
// 已经有回显的签名 // 已经有回显的签名(云端或本地草稿)
if (!signatureServerPath.value) { if (!signatureServerPath.value && !signatureLocalPath.value) {
uni.showToast({ uni.showToast({
title: '请进行电子签名', title: '请进行电子签名',
icon: 'none' icon: 'none'
@@ -822,7 +887,18 @@
} }
isSubmitting.value = true; isSubmitting.value = true;
uni.showLoading({ title: '正在提交...', mask: true }); uni.showLoading({ title: '正在提交...', mask: true });
try {
if (!signatureServerPath.value && signatureLocalPath.value) {
const { url } = await uploadToCloud(signatureLocalPath.value);
applySignatureFromServer(url);
}
await executeSubmit(); await executeSubmit();
} catch (err) {
isSubmitting.value = false;
uni.hideLoading();
console.error('签名上传失败:', err);
uni.showToast({ title: '签名上传失败,请重试', icon: 'none' });
}
} }
}; };
@@ -1001,42 +1077,38 @@
} }
}; };
// 草稿缓存与恢复逻辑 (移至底部以确保 formData 等响应式状态已被正常定义) const signaturePaths = ref([]);
const hasDraft = ref(false);
const showRestoreBanner = ref(false); // 独立控制提示 Banner仅在初次确实从本地恢复了内容时才显示
const isRestoring = ref(false); // 正在恢复标志避免触发冗余watch
const signaturePaths = ref([]); // 缓存手写签名的绘制路径
const getDraftKey = () => `draft_rectify_${hazardId.value || ''}_${rectifyId.value || ''}`;
// 电子签名画布手写线条变动回调 const rectifyDraftHasContent = (data) => {
const onSignatureChange = () => { const form = data.formData || {};
isSignatureEmpty.value = false; return !!(
signaturePaths.value = []; form.rectifyPlan ||
if (hazardId.value || rectifyId.value) { form.rectificationMeasures ||
saveDraft(); form.controlMeasures ||
} form.rectifyResult ||
form.planCost ||
form.actualCost ||
(data.fileList1 && data.fileList1.length > 0) ||
data.signatureServerPath ||
data.signatureLocalPath ||
(data.signaturePaths && data.signaturePaths.length > 0)
);
}; };
// 保存草稿 (排除选择器人员缓存,仅缓存方案、情况、金额、图片、签名等输入信息) const {
const saveDraft = () => { showRestoreBanner,
if (isRestoring.value) return; clear: clearDraft,
const key = getDraftKey(); restore: restoreDraft,
const hasContent = formData.rectifyPlan || save: saveDraft,
formData.rectificationMeasures || bindAutoSave
formData.controlMeasures || } = useDraftCache({
formData.rectifyResult || getKey: () => buildDraftKey(DRAFT_NS.RECTIFY, hazardId.value, rectifyId.value),
formData.planCost || getFallbackKeys: () => {
formData.actualCost || const primary = buildDraftKey(DRAFT_NS.RECTIFY, hazardId.value, rectifyId.value);
fileList1.value.length > 0 || const compact = buildDraftKeyCompact(DRAFT_NS.RECTIFY, hazardId.value, rectifyId.value);
signatureServerPath.value || return compact !== primary ? [compact] : [];
signaturePaths.value.length > 0; },
if (!hasContent) { getPayload: () => ({
uni.removeStorageSync(key);
hasDraft.value = false;
return;
}
const data = {
formData: { formData: {
rectifyPlan: formData.rectifyPlan, rectifyPlan: formData.rectifyPlan,
rectificationMeasures: formData.rectificationMeasures, rectificationMeasures: formData.rectificationMeasures,
@@ -1048,21 +1120,30 @@
fileList1: fileList1.value, fileList1: fileList1.value,
signatureServerPath: signatureServerPath.value, signatureServerPath: signatureServerPath.value,
signatureUrl: signatureUrl.value, signatureUrl: signatureUrl.value,
signatureLocalPath: signatureLocalPath.value,
showCanvas: showCanvas.value, showCanvas: showCanvas.value,
signaturePaths: signaturePaths.value signaturePaths: signaturePaths.value
}; }),
uni.setStorageSync(key, JSON.stringify(data)); hasContent: rectifyDraftHasContent,
hasDraft.value = true; applyPayload: (data) => {
}; const form = data.formData || {};
formData.rectifyPlan = form.rectifyPlan || '';
// 清空草稿 formData.rectificationMeasures = form.rectificationMeasures || '';
const clearDraft = (showToast = true) => { formData.controlMeasures = form.controlMeasures || '';
const key = getDraftKey(); formData.rectifyResult = form.rectifyResult || '';
uni.removeStorageSync(key); formData.planCost = form.planCost || '';
hasDraft.value = false; formData.actualCost = form.actualCost || '';
showRestoreBanner.value = false; fileList1.value = data.fileList1 || [];
signaturePaths.value = data.signaturePaths || [];
isRestoring.value = true; if (data.signatureServerPath || data.signatureUrl) {
applySignatureFromServer(data.signatureServerPath || data.signatureUrl);
} else if (data.signatureLocalPath) {
applySignatureFromLocal(data.signatureLocalPath);
} else if (data.showCanvas === false) {
showCanvas.value = false;
}
},
clearForm: () => {
formData.rectifyPlan = ''; formData.rectifyPlan = '';
formData.rectificationMeasures = ''; formData.rectificationMeasures = '';
formData.controlMeasures = ''; formData.controlMeasures = '';
@@ -1072,85 +1153,29 @@
fileList1.value = []; fileList1.value = [];
signatureServerPath.value = ''; signatureServerPath.value = '';
signatureUrl.value = ''; signatureUrl.value = '';
signatureLocalPath.value = '';
showCanvas.value = true; showCanvas.value = true;
signaturePaths.value = []; signaturePaths.value = [];
if (signatureRef.value) { if (signatureRef.value) {
signatureRef.value.clear(); signatureRef.value.clear();
} }
},
nextTick(() => { canSave: () => !!(hazardId.value || rectifyId.value),
isRestoring.value = false; onAfterRestore: (data) => {
}); if (data.signatureServerPath || data.signatureUrl || data.signatureLocalPath) {
if (showToast) { return;
uni.showToast({ title: '草稿已清空', icon: 'none' });
} }
}; if (data.signaturePaths?.length > 0) {
// 恢复草稿 (不恢复任何选择器数据)
const restoreDraft = () => {
const key = getDraftKey();
const cached = uni.getStorageSync(key);
if (cached) {
try {
const data = JSON.parse(cached);
const hasContent = data.formData.rectifyPlan ||
data.formData.rectificationMeasures ||
data.formData.controlMeasures ||
data.formData.rectifyResult ||
data.formData.planCost ||
data.formData.actualCost ||
(data.fileList1 && data.fileList1.length > 0) ||
data.signatureServerPath ||
(data.signaturePaths && data.signaturePaths.length > 0);
if (!hasContent) return;
isRestoring.value = true;
formData.rectifyPlan = data.formData.rectifyPlan || '';
formData.rectificationMeasures = data.formData.rectificationMeasures || '';
formData.controlMeasures = data.formData.controlMeasures || '';
formData.rectifyResult = data.formData.rectifyResult || '';
formData.planCost = data.formData.planCost || '';
formData.actualCost = data.formData.actualCost || '';
fileList1.value = data.fileList1 || [];
signaturePaths.value = data.signaturePaths || [];
// 优先用草稿里已上传的完整 URL否则保持画板模式
if (data.signatureServerPath || data.signatureUrl) {
applySignatureFromServer(data.signatureServerPath || data.signatureUrl);
} else if (data.showCanvas === false) {
showCanvas.value = false;
}
hasDraft.value = true;
showRestoreBanner.value = true; // 确实存在内容并恢复了,才亮起提示 Banner
// 延迟恢复签名画布线条重绘,等待 Canvas 组件完全加载完毕
if (signaturePaths.value.length > 0) {
setTimeout(() => { setTimeout(() => {
if (signatureRef.value) { if (signatureRef.value) {
isSignatureEmpty.value = false; isSignatureEmpty.value = false;
// wot-design-uni auto rendering
} }
}, 450); }, 450);
} }
}
nextTick(() => {
isRestoring.value = false;
}); });
uni.showToast({ bindAutoSave(() => [
title: '已自动恢复您上次未提交的内容',
icon: 'none',
duration: 2500
});
} catch (e) {
console.error('解析草稿失败:', e);
isRestoring.value = false;
}
}
};
// 深度监听表单项和签名笔画变化,自动保存草稿
watch(
() => [
formData.rectifyPlan, formData.rectifyPlan,
formData.rectificationMeasures, formData.rectificationMeasures,
formData.controlMeasures, formData.controlMeasures,
@@ -1159,15 +1184,19 @@
formData.actualCost, formData.actualCost,
fileList1.value, fileList1.value,
signatureServerPath.value, signatureServerPath.value,
signatureUrl.value,
signatureLocalPath.value,
showCanvas.value,
signaturePaths.value signaturePaths.value
], ]);
() => {
if (hazardId.value || rectifyId.value) { onHide(() => {
saveDraft(); if (showCanvas.value && !isSignatureEmpty.value && signatureRef.value && !isDraftExporting.value) {
isDraftExporting.value = true;
signatureRef.value.confirm();
} }
}, saveDraft();
{ deep: true } });
);
onLoad((options) => { onLoad((options) => {
// 计算签名画布宽度 // 计算签名画布宽度

View File

@@ -1,196 +1,50 @@
<template> <template>
<view class="padding page"> <view class="page">
<view class="padding bg-white radius"> <HazardDetailPanel :detail="detailData" :loading="loading" />
<view class="flex margin-bottom">
<view class="text-gray">检查形式</view>
<view class="text-red">*</view>
</view>
<view class="read-only-box">{{ detailData.source || '暂无' }}</view>
<view class="flex margin-bottom margin-top">
<view class="text-gray">隐患图片</view>
<view class="text-red">*</view>
</view>
<view class="margin-bottom">
<view v-if="detailData.attachments && detailData.attachments.length > 0" class="margin-top-xs">
<view class="flex" style="flex-wrap: wrap; gap: 10rpx;">
<image
v-for="(img, idx) in detailData.attachments"
:key="idx"
:src="getFullPath(img.filePath)"
style="width: 136rpx;height: 136rpx;border-radius: 16rpx;"
mode="aspectFill"
@click="previewHazardImage(idx)"
></image>
</view>
</view>
<view v-else class="text-gray text-sm">暂无图片</view>
<view class="text-gray text-sm margin-top-xs">必填请上传现场照片作为隐患证据</view>
</view>
<view class="flex margin-bottom margin-top">
<view class="text-gray">隐患标题</view>
<view class="text-red">*</view>
</view>
<up-input v-model="detailData.title" disabled disabledColor="#F6F6F6" border="surround" placeholder="暂无" />
<view class="text-sm text-gray margin-top-xs">请用简洁的语言概括隐患要点</view>
<view class="flex margin-bottom margin-top">
<view class="text-gray">隐患等级</view>
<view class="text-red">*</view>
</view>
<view class="flex col-2" style="gap: 10rpx;">
<view :class="detailData.level === 2 ? 'bg-blue light' : 'bg-gray'" class="level-item">一般隐患</view>
<view :class="detailData.level === 3 ? 'bg-blue light' : 'bg-gray'" class="level-item">重大隐患</view>
</view>
<view class="flex margin-bottom margin-top">
<view class="text-gray">隐患位置</view>
<view class="text-red">*</view>
</view>
<up-input v-model="detailData.address" disabled disabledColor="#F6F6F6" border="surround" placeholder="暂无地址" />
<view class="text-gray text-sm margin-top-xs">办公楼3层东侧消防通道生产车间A区设备旁等或点击"选择地址"按钮在地图上选择</view>
<view class="flex margin-bottom margin-top">
<view class="text-gray">法律依据</view>
</view>
<view class="read-only-select">
<view class="select-value" :class="{ placeholder: !legalBasisText }">
{{ legalBasisText || '暂无' }}
</view>
</view>
<view class="flex margin-bottom margin-top">
<view class="text-gray">隐患区域</view>
</view>
<view class="read-only-select">
<view class="flex align-center">
<view
v-if="detailData.areaColor"
class="area-color-dot"
:style="{ backgroundColor: detailData.areaColor }"
></view>
<view class="select-value" :class="{ placeholder: !detailData.areaName }">
{{ detailData.areaName || '暂无' }}
</view>
</view>
</view>
<view class="flex margin-bottom margin-top">
<view class="text-gray">隐患描述</view>
<view class="text-red">*</view>
</view>
<up-textarea v-model="detailData.description" placeholder="暂无描述" disabled autoHeight></up-textarea>
<view class="text-gray text-sm margin-top-xs">请详细说明隐患现状潜在风险及影响范围</view>
<view class="text-gray margin-bottom margin-top">隐患标签</view>
<view class="read-only-box">{{ detailData.tagName || '暂无' }}</view>
</view>
</view> </view>
</template> </template>
<script setup> <script setup>
import { ref, reactive, computed } from 'vue' import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app';
import { getHiddenDangerDetail } from '@/request/api.js' import HazardDetailPanel from '@/components/hazardDetail/HazardDetailPanel.vue';
import { toImageUrl } from '@/request/request.js' import { getHazardDetail } from '@/request/api.js';
const detailData = reactive({ const detailData = ref({});
hazardId: '', const loading = ref(false);
assignId: '',
title: '',
level: 0,
levelName: '',
source: '',
description: '',
address: '',
areaName: '',
areaColor: '',
tagName: '',
legalBasis: '',
regulationName: '',
attachments: []
})
const legalBasisText = computed(() => detailData.legalBasis || detailData.regulationName || '') const loadDetail = async (hazardId) => {
loading.value = true;
const getFullPath = (filePath) => toImageUrl(filePath)
const previewHazardImage = (index) => {
if (!detailData.attachments || detailData.attachments.length === 0) return
uni.previewImage({
current: index,
urls: detailData.attachments.map(item => getFullPath(item.filePath))
})
}
const fetchDetail = async (hazardId, assignId) => {
try { try {
const params = { hazardId } const res = await getHazardDetail(hazardId);
if (assignId) params.assignId = assignId
const res = await getHiddenDangerDetail(params)
if (res.code === 0 && res.data) { if (res.code === 0 && res.data) {
Object.assign(detailData, res.data) detailData.value = res.data;
} else { } else {
uni.showToast({ title: res.msg || '获取详情失败', icon: 'none' }) uni.showToast({ title: res.msg || '获取详情失败', icon: 'none' });
} }
} catch (error) { } catch (error) {
console.error('获取隐患详情失败:', error) console.error('获取隐患详情失败:', error);
uni.showToast({ title: '请求失败', icon: 'none' }) } finally {
loading.value = false;
} }
};
onLoad((options) => {
if (options.hazardId) {
loadDetail(options.hazardId);
return;
} }
onLoad((options) => { uni.showToast({ title: '缺少隐患ID', icon: 'none' });
if (options.hazardId) { setTimeout(() => {
fetchDetail(options.hazardId, options.assignId) uni.navigateBack();
} }, 1500);
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.page { .page {
min-height: 100vh; min-height: 100vh;
background: #EBF2FC; background: #ebf2fc;
} }
.read-only-box {
background: #f5f5f5;
border-radius: 8rpx;
padding: 20rpx 24rpx;
font-size: 28rpx;
color: #333;
}
.read-only-select {
background: #f5f5f5;
border: 1rpx solid #dcdfe6;
border-radius: 8rpx;
padding: 20rpx 24rpx;
.select-value {
font-size: 28rpx;
color: #333;
line-height: 1.5;
word-break: break-all;
&.placeholder {
color: #999;
}
}
}
.level-item {
padding: 16rpx 40rpx;
border-radius: 8rpx;
text-align: center;
font-size: 28rpx;
}
.area-color-dot {
width: 24rpx;
height: 24rpx;
border-radius: 50%;
margin-right: 16rpx;
flex-shrink: 0;
}
</style> </style>

View File

@@ -6,7 +6,7 @@
<image class="header-bg-image" src="/static/home_icon/jianbianbeijing.png" mode="aspectFill"></image> <image class="header-bg-image" src="/static/home_icon/jianbianbeijing.png" mode="aspectFill"></image>
<!-- 自定义导航栏 --> <!-- 自定义导航栏 -->
<u-navbar <u-navbar
title="三查一曝光" title="湘西州“三个一”安全管理平台"
:placeholder="false" :placeholder="false"
:fixed="false" :fixed="false"
:safeAreaInsetTop="true" :safeAreaInsetTop="true"
@@ -45,23 +45,30 @@
</view> </view>
<!-- 我的检查计划 --> <!-- 我的检查计划 -->
<view class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -30rpx; margin-right: -30rpx;"> <view class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -30rpx; margin-right: -30rpx;">
<view class="flex margin-bottom-xl"> <view class="flex margin-bottom-xl align-center justify-between">
<!-- <view class="border-tite"></view> -->
<view class="text-bold margin-left-xs" style="font-size: 32rpx;">我的检查计划</view> <view class="text-bold margin-left-xs" style="font-size: 32rpx;">我的检查计划</view>
<!-- <button class="cu-btn round sm line-blue inspect-list-btn" @click="goInspectList">检查列表</button> -->
</view> </view>
<!-- 无数据提示 --> <!-- 无数据提示 -->
<view v-if="checkPlanData.length === 0" class="text-center text-gray padding"> <view v-if="checkPlanData.length === 0" class="text-center text-gray padding">
暂无检查计划 暂无检查计划
</view> </view>
<!-- 列表渲染 --> <!-- 列表渲染 -->
<view class="plan-card margin-bottom" v-for="(item, index) in checkPlanData" :key="item.id"> <view class="plan-card margin-bottom" v-for="item in checkPlanData" :key="item.id">
<!-- 蓝色标题栏 --> <!-- 蓝色标题栏 -->
<view class="plan-header"> <view class="plan-header" @click="togglePlanExpand(item.id)">
<!-- <image src="/static/蒙版组 273.png" class="plan-header-icon"></image> -->
<text class="plan-header-title">{{ item.name }}</text> <text class="plan-header-title">{{ item.name }}</text>
<view class="plan-toggle-btn" @click.stop="togglePlanExpand(item.id)">
<u-icon
:name="isPlanExpanded(item.id) ? 'arrow-down' : 'arrow-up'"
color="#ffffff"
size="18"
></u-icon>
</view> </view>
<!-- 内容区域 --> </view>
<view class="plan-body"> <!-- 内容区域收起时保留标签与计划时间 -->
<view class="plan-body" :class="{ 'plan-body--collapsed': !isPlanExpanded(item.id) }">
<view class="plan-body-summary">
<view class="flex"> <view class="flex">
<view class="border-border margin-right-xs">{{ item.runModeName }}完成</view> <view class="border-border margin-right-xs">{{ item.runModeName }}完成</view>
<view class="border-border">{{ item.cycle }}</view> <view class="border-border">{{ item.cycle }}</view>
@@ -70,41 +77,50 @@
<view>计划时间</view> <view>计划时间</view>
<view style="color: #333333;">{{ formatDate(item.planStartTime) }}{{ formatDate(item.planEndTime) }}</view> <view style="color: #333333;">{{ formatDate(item.planStartTime) }}{{ formatDate(item.planEndTime) }}</view>
</view> </view>
</view>
<view v-show="isPlanExpanded(item.id)" class="plan-body-detail">
<view class="flex margin-top align-center"> <view class="flex margin-top align-center">
<view style="color: #B5B5B5;">完成进度</view> <view style="color: #B5B5B5;">完成进度</view>
<view class="flex align-center margin-left-sm"> <view class="flex align-center margin-left-sm">
<view class="cu-progress round"> <view class="cu-progress round">
<view class="bg-green" :style="{ width: item.progress + '%' }"></view> <view class="bg-green" :style="{ width: formatProgress(item.progress) + '%' }"></view>
</view> </view>
<text class="margin-left-sm">{{ item.progress }}%</text> <text class="margin-left-sm">{{ formatProgress(item.progress) }}%</text>
</view> </view>
</view> </view>
<view class="plan-stats margin-top"> <view class="plan-stats margin-top">
<view class="plan-stat-item"> <view class="plan-stat-item">
<view class="plan-stat-num text-orange">{{ item.totalCount }}</view> <view class="plan-stat-num text-orange">{{ item.pendingCount ?? 0 }}</view>
<view class="plan-stat-label">排查项</view> <view class="plan-stat-label">未完成</view>
</view> </view>
<view class="plan-stat-item"> <view class="plan-stat-item">
<view class="plan-stat-num text-yellow">{{ item.totalCount - item.finishedCount }}</view> <view class="plan-stat-num text-yellow">{{ item.waitCheckNum ?? 0 }}</view>
<view class="plan-stat-label">待排查</view> <view class="plan-stat-label">待排查</view>
</view> </view>
<view class="plan-stat-item"> <view class="plan-stat-item">
<view class="plan-stat-num text-olive">0</view> <view class="plan-stat-num text-red">{{ item.unusualNum ?? 0 }}</view>
<view class="plan-stat-label">待验收</view> <view class="plan-stat-label">异常数</view>
</view> </view>
<view class="plan-stat-item"> <view class="plan-stat-item">
<view class="plan-stat-num text-blue">{{ item.finishedCount }}</view> <view class="plan-stat-num text-blue">{{ item.finishedCount ?? 0 }}</view>
<view class="plan-stat-label">已完成</view> <view class="plan-stat-label">已完成</view>
</view> </view>
</view> </view>
<view class="margin-top margin-bottom flex justify-end"> <view class="margin-top margin-bottom flex justify-end">
<button class="cu-btn round lg light bg-blue margin-right" @click.stop="ViewDetails(item)">看详情</button> <button class="cu-btn round lg light bg-blue margin-right" @click.stop="goTodayInspect(item)">今日检</button>
<button v-if="item.finishedCount < item.totalCount" class="cu-btn round lg bg-blue" @click.stop="goDetails(item)">开始检查</button> <button class="cu-btn round lg bg-blue" @click.stop="goPlanCheckList(item)">检查清单</button>
<view v-else class="cu-btn round lg bg-green">已完成</view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<view
v-if="hasMoreCheckPlans"
class="plan-load-more"
@click="loadMoreCheckPlans"
>
加载更多
</view>
</view>
<!-- 我的隐患 --> <!-- 我的隐患 -->
<view class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -40rpx; margin-right: -40rpx;"> <view class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -40rpx; margin-right: -40rpx;">
<view class="flex margin-bottom"> <view class="flex margin-bottom">
@@ -177,7 +193,7 @@
<script setup> <script setup>
import { ref, reactive, computed } from 'vue'; import { ref, reactive, computed } from 'vue';
// import { onLoad } from '@dcloudio/uni-app'; // import { onLoad } from '@dcloudio/uni-app';
import {getCheckPlanList,getHiddenDangerList} from '@/request/api.js' import {getCheckPlanList, getHiddenDangerList} from '@/request/api.js'
import { getProfileDetail } from '@/request/three_one_api/info.js'; import { getProfileDetail } from '@/request/three_one_api/info.js';
import { onLoad, onShow } from '@dcloudio/uni-app'; import { onLoad, onShow } from '@dcloudio/uni-app';
import { toImageUrl } from '@/request/request.js'; import { toImageUrl } from '@/request/request.js';
@@ -293,15 +309,50 @@
// admin、manage 及其他角色展示全部菜单 // admin、manage 及其他角色展示全部菜单
return allMenuList; return allMenuList;
}); });
const ViewDetails = (item) => { const goInspectList = () => {
uni.navigateTo({ uni.navigateTo({
url: `/pages/plandetail/plandetail?id=${item.id}` url: '/pages/Inspectionresult/list'
}) })
} }
const goDetails = (item) => {
const formatProgress = (progress) => {
const num = Number(progress);
if (Number.isNaN(num)) return 0;
return Math.min(100, Math.max(0, num));
}
const getTodayDateStr = () => {
const date = new Date();
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
};
const goTodayInspect = (item) => {
const oneTableId = item.id;
const taskDate = getTodayDateStr();
const tableId = item.tableId ?? item.id;
if (!oneTableId || !taskDate) {
uni.showToast({ title: '缺少任务参数', icon: 'none' });
return;
}
const name = item.name || '';
const tableIdQuery = tableId ? `&tableId=${tableId}` : '';
uni.navigateTo({ uni.navigateTo({
url: `/pages/Inspectionresult/Inspectionresult?id=${item.id}` url: `/pages/Inspectionresult/detail?oneTableId=${oneTableId}&taskDate=${encodeURIComponent(taskDate)}&name=${encodeURIComponent(name)}${tableIdQuery}`
}) });
}
const goPlanCheckList = (item) => {
const tableId = item.tableId ?? item.id;
if (!tableId) {
uni.showToast({ title: '缺少检查表ID', icon: 'none' });
return;
}
uni.navigateTo({
url: `/pages/Inspectionresult/list?tableId=${tableId}&name=${encodeURIComponent(item.name || '')}`
});
} }
// 菜单点击跳转 // 菜单点击跳转
const handleMenuClick = (item) => { const handleMenuClick = (item) => {
@@ -326,19 +377,55 @@
} }
//我的检查计划 //我的检查计划
const PLAN_INITIAL_SIZE = 4;
const checkPlanParams = ref({ const checkPlanParams = ref({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: PLAN_INITIAL_SIZE,
name: '' name: ''
}); });
const checkPlanData = ref([]); const checkPlanData = ref([]);
const checkPlanTotal = ref(0);
const planShowAll = ref(false);
const expandedPlanMap = ref({});
const hasMoreCheckPlans = computed(() => {
return !planShowAll.value && checkPlanTotal.value > PLAN_INITIAL_SIZE;
});
const initPlanExpandedState = (records = [], { reset = false, expandFirst = false } = {}) => {
const map = reset ? {} : { ...expandedPlanMap.value };
records.forEach((item, index) => {
if (reset) {
map[item.id] = expandFirst && index === 0;
} else if (!(item.id in map)) {
map[item.id] = false;
}
});
expandedPlanMap.value = map;
};
const isPlanExpanded = (id) => !!expandedPlanMap.value[id];
const togglePlanExpand = (id) => {
expandedPlanMap.value = {
...expandedPlanMap.value,
[id]: !expandedPlanMap.value[id]
};
};
const getCheckPlanLists = async () => { const getCheckPlanLists = async () => {
planShowAll.value = false;
try { try {
const res = await getCheckPlanList(checkPlanParams.value); const res = await getCheckPlanList({
console.log(res); pageNum: 1,
pageSize: PLAN_INITIAL_SIZE,
name: checkPlanParams.value.name
});
if (res.code === 0) { if (res.code === 0) {
checkPlanData.value = res.data.records; const records = res.data?.records || [];
checkPlanTotal.value = Number(res.data?.total ?? records.length);
checkPlanData.value = records;
initPlanExpandedState(records, { reset: true, expandFirst: true });
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@@ -347,6 +434,28 @@
} }
}; };
const loadMoreCheckPlans = async () => {
if (!hasMoreCheckPlans.value) return;
try {
const res = await getCheckPlanList({
pageNum: 1,
pageSize: checkPlanTotal.value,
name: checkPlanParams.value.name
});
if (res.code === 0) {
const records = res.data?.records || [];
initPlanExpandedState(records);
checkPlanData.value = records;
planShowAll.value = true;
} else {
uni.showToast({ title: res.msg || '加载失败', icon: 'none' });
}
} catch (error) {
console.error(error);
uni.showToast({ title: '加载失败', icon: 'none' });
}
};
// 格式化日期 (2025-12-18 00:00:00 -> 2025-12-18) // 格式化日期 (2025-12-18 00:00:00 -> 2025-12-18)
const formatDate = (dateStr) => { const formatDate = (dateStr) => {
if (!dateStr) return ''; if (!dateStr) return '';
@@ -422,7 +531,7 @@
// 查看隐患详情 // 查看隐患详情
const viewHazardDetail = (item) => { const viewHazardDetail = (item) => {
uni.navigateTo({ uni.navigateTo({
url: `/pages/hiddendanger/view?hazardId=${item.hazardId}&assignId=${item.assignId}` url: `/pages/hiddendanger/detail2?hazardId=${item.hazardId}&assignId=${item.assignId || ''}`
}) })
} }
@@ -605,6 +714,15 @@
} }
// ========== 检查计划卡片 ========== // ========== 检查计划卡片 ==========
.inspect-list-btn {
margin: 0;
padding: 0 24rpx;
height: 56rpx;
line-height: 56rpx;
font-size: 24rpx;
flex-shrink: 0;
}
.plan-card { .plan-card {
border-radius: 16rpx; border-radius: 16rpx;
overflow: hidden; overflow: hidden;
@@ -616,6 +734,7 @@
padding: 24rpx 30rpx; padding: 24rpx 30rpx;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
.plan-header-icon { .plan-header-icon {
width: 36rpx; width: 36rpx;
@@ -624,15 +743,38 @@
} }
.plan-header-title { .plan-header-title {
flex: 1;
min-width: 0;
padding-right: 16rpx;
color: #fff; color: #fff;
font-size: 30rpx; font-size: 30rpx;
font-weight: bold; font-weight: bold;
} }
} }
.plan-toggle-btn {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 48rpx;
height: 48rpx;
}
.plan-body { .plan-body {
padding: 24rpx 30rpx 10rpx 30rpx; padding: 24rpx 30rpx 10rpx 30rpx;
background: #fff; background: #fff;
&--collapsed {
padding-bottom: 24rpx;
}
}
.plan-load-more {
text-align: center;
padding: 24rpx 0 8rpx;
font-size: 28rpx;
color: #2667E9;
} }
.plan-stats { .plan-stats {

View File

@@ -4,7 +4,7 @@
<image src="/static/index/index_bg.png" class="bg-image"></image> <image src="/static/index/index_bg.png" class="bg-image"></image>
<view class="padding login"> <view class="padding login">
<view class="text-xl text-black text-bold">账号登录</view> <view class="text-xl text-black text-bold">账号登录</view>
<view class="padding-top">欢迎登录三查一曝光平台</view> <view class="padding-top">欢迎登录湘西州三个一安全管理平台</view>
</view> </view>
</view> </view>

View File

@@ -39,7 +39,7 @@
<!-- 添加成员按钮 --> <!-- 添加成员按钮 -->
<view class="add-btn-wrapper"> <view class="add-btn-wrapper">
<button class="add-btn" @click="showPopup = true"> <button class="add-btn" @click="openAddMemberPopup">
<text class="cuIcon-add"></text> <text class="cuIcon-add"></text>
<text>添加成员</text> <text>添加成员</text>
</button> </button>
@@ -90,6 +90,17 @@
</view> </view>
</view> </view>
<!-- 岗位 -->
<view class="form-item">
<view class="form-label">岗位</view>
<view class="form-select" @click="openPostPicker">
<text :class="selectedPostName ? '' : 'text-gray'">
{{ selectedPostName || postPlaceholder }}
</text>
<text class="cuIcon-unfold"></text>
</view>
</view>
<!-- 占位空白防止被底部按钮遮挡 --> <!-- 占位空白防止被底部按钮遮挡 -->
<view style="height: 40rpx;"></view> <view style="height: 40rpx;"></view>
</scroll-view> </scroll-view>
@@ -101,6 +112,14 @@
</view> </view>
</u-popup> </u-popup>
<up-picker
:show="showPostPicker"
:columns="postColumns"
@confirm="onPostConfirm"
@cancel="showPostPicker = false"
@close="showPostPicker = false"
></up-picker>
<up-picker <up-picker
:show="showRolePicker" :show="showRolePicker"
:columns="roleColumns" :columns="roleColumns"
@@ -115,7 +134,7 @@
<script setup> <script setup>
import { ref, reactive, computed, onMounted } from 'vue'; import { ref, reactive, computed, onMounted } from 'vue';
import { addMember, getMemberList, lockOrUnlockMember } from '@/request/api.js'; import { addMember, getMemberList, lockOrUnlockMember, getSystemUserFormOptions, listPostByDeptId } from '@/request/api.js';
// 用户信息从storage获取 // 用户信息从storage获取
const userInfo = ref({ const userInfo = ref({
@@ -170,7 +189,9 @@ const fetchMemberList = async () => {
// 弹窗控制 // 弹窗控制
const showPopup = ref(false); const showPopup = ref(false);
const showRolePicker = ref(false); const showRolePicker = ref(false);
const showPostPicker = ref(false);
const selectedRoleName = ref(''); const selectedRoleName = ref('');
const selectedPostName = ref('');
// 表单数据 // 表单数据
const formData = reactive({ const formData = reactive({
@@ -178,29 +199,93 @@ const formData = reactive({
nickname: '', nickname: '',
phone: '', phone: '',
password: '', password: '',
roleType: '' roleType: '',
postId: ''
}); });
// 角色类型选择器数据 // 角色选项(接口返回)
const roleColumns = reactive([ const roleOptions = ref([]);
['管理员', '普通成员'] const roleColumns = ref([[]]);
]);
// 角色名称与值的映射 // 岗位选项按部门ID查询
const roleMap = { const postOptions = ref([]);
'管理员': 'manage', const postColumns = ref([[]]);
'普通成员': 'common'
const postPlaceholder = computed(() => {
if (!userInfo.value.deptId) return '请先登录并确认部门信息';
return postOptions.value.length ? '请选择岗位' : '暂无可用岗位';
});
// 打开添加成员弹窗:拉取角色和岗位列表
const openAddMemberPopup = async () => {
getUserInfo();
const deptId = userInfo.value.deptId;
if (!deptId) {
uni.showToast({ title: '缺少部门信息,无法添加成员', icon: 'none' });
return;
}
try {
uni.showLoading({ title: '加载中...' });
const [optionsRes, postRes] = await Promise.all([
getSystemUserFormOptions(),
listPostByDeptId(deptId)
]);
uni.hideLoading();
if (optionsRes.code !== 0 || !Array.isArray(optionsRes.roles) || !optionsRes.roles.length) {
uni.showToast({ title: optionsRes.msg || '获取角色列表失败', icon: 'none' });
return;
}
roleOptions.value = optionsRes.roles.filter((item) => item.status === '0');
roleColumns.value = [roleOptions.value.map((item) => item.roleName)];
const posts = postRes.data || postRes.rows || [];
postOptions.value = posts.filter((item) => item.status === '0');
postColumns.value = [postOptions.value.map((item) => item.postName)];
showPopup.value = true;
} catch (error) {
uni.hideLoading();
console.error('获取表单选项失败:', error);
uni.showToast({
title: error?.msg || '获取表单选项失败',
icon: 'none'
});
}
};
const openPostPicker = () => {
if (!postOptions.value.length) {
uni.showToast({ title: '当前部门暂无可用岗位', icon: 'none' });
return;
}
showPostPicker.value = true;
}; };
// 角色类型选择确认 // 角色类型选择确认
const onRoleConfirm = (e) => { const onRoleConfirm = (e) => {
if (e.value && e.value.length > 0) { if (e.value && e.value.length > 0) {
selectedRoleName.value = e.value[0]; const roleName = e.value[0];
formData.roleType = roleMap[e.value[0]]; const role = roleOptions.value.find((item) => item.roleName === roleName);
selectedRoleName.value = roleName;
formData.roleType = role?.roleKey || '';
} }
showRolePicker.value = false; showRolePicker.value = false;
}; };
// 岗位选择确认
const onPostConfirm = (e) => {
if (e.value && e.value.length > 0) {
const postName = e.value[0];
const post = postOptions.value.find((item) => item.postName === postName);
selectedPostName.value = postName;
formData.postId = post?.postId ?? '';
}
showPostPicker.value = false;
};
// 重置表单 // 重置表单
const resetForm = () => { const resetForm = () => {
formData.username = ''; formData.username = '';
@@ -208,7 +293,11 @@ const resetForm = () => {
formData.phone = ''; formData.phone = '';
formData.password = ''; formData.password = '';
formData.roleType = ''; formData.roleType = '';
formData.postId = '';
selectedRoleName.value = ''; selectedRoleName.value = '';
selectedPostName.value = '';
postOptions.value = [];
postColumns.value = [[]];
}; };
// 提交表单 // 提交表单
@@ -226,13 +315,24 @@ const handleSubmit = async () => {
return; return;
} }
getUserInfo();
const deptId = userInfo.value.deptId;
if (!deptId) {
uni.showToast({ title: '缺少部门信息,无法添加成员', icon: 'none' });
return;
}
const params = { const params = {
userName: formData.username, userName: formData.username,
nickName: formData.nickname || '', nickName: formData.nickname || '',
phonenumber: formData.phone || '', phonenumber: formData.phone || '',
password: formData.password, password: formData.password,
roleType: formData.roleType roleType: formData.roleType,
deptId
}; };
if (formData.postId) {
params.postId = formData.postId;
}
try { try {
const res = await addMember(params); const res = await addMember(params);

View File

@@ -7,6 +7,15 @@ export function getCheckPlanList(params) {
data: params data: params
}); });
} }
// 获取检查计划详情-按任务(每天/每周/每月/每季度)
export function getPlanTableDetail(params) {
return requestAPI({
url: '/frontend/plan/tableDetail',
method: 'GET',
data: params
});
}
//进入巡检(获取第一个未完成的任务) //进入巡检(获取第一个未完成的任务)
export function enterCheckPlan(oneTableId) { export function enterCheckPlan(oneTableId) {
return requestAPI({ return requestAPI({
@@ -14,6 +23,15 @@ export function enterCheckPlan(oneTableId) {
method: 'GET' method: 'GET'
}); });
} }
// 进入巡检(一次性获取全部任务)
export function getAllTask(oneTableId, taskDate) {
const date = String(taskDate || '').split(' ')[0];
return requestAPI({
url: `/frontend/task/getAllTask/${oneTableId}/${date}`,
method: 'GET'
});
}
//获取指定任务详情 //获取指定任务详情
export function getCheckTaskDetail(taskId) { export function getCheckTaskDetail(taskId) {
return requestAPI({ return requestAPI({
@@ -29,6 +47,14 @@ export function submitCheckResult(params) {
data: params data: params
}); });
} }
// 批量提交巡检结果
export function submitAllTask(data) {
return requestAPI({
url: '/frontend/task/submitAll',
method: 'POST',
data
});
}
//新增隐患 //新增隐患
export function addHiddenDanger(params) { export function addHiddenDanger(params) {
return requestAPI({ return requestAPI({
@@ -71,7 +97,7 @@ export function getMyHiddenDangerList(params) {
data: params data: params
}); });
} }
//获取隐患详情 //获取隐患详情(小程序流程页:整改/验收等,支持 assignId
export function getHiddenDangerDetail(params) { export function getHiddenDangerDetail(params) {
// 过滤掉 assignId 为 null、undefined、'null'、空字符串的情况 // 过滤掉 assignId 为 null、undefined、'null'、空字符串的情况
const filteredParams = { ...params }; const filteredParams = { ...params };
@@ -84,6 +110,14 @@ export function getHiddenDangerDetail(params) {
data: filteredParams data: filteredParams
}); });
} }
// 获取隐患详情(详情页:完整历史流程)
export function getHazardDetail(hazardId) {
return requestAPI({
url: `/admin/hazard/detail/${hazardId}`,
method: 'GET'
});
}
//获取隐患排查列表 //获取隐患排查列表
export function getHiddenDangerList(params) { export function getHiddenDangerList(params) {
return requestAPI({ return requestAPI({
@@ -169,6 +203,22 @@ export function lockOrUnlockMember(params) {
data: params data: params
}); });
} }
// 获取添加成员表单选项(角色、岗位等)
export function getSystemUserFormOptions() {
return requestAPI({
url: '/system/user/',
method: 'GET',
loadingText: false
});
}
// 根据部门ID获取岗位列表
export function listPostByDeptId(deptId) {
return requestAPI({
url: `/system/post/listByDeptId/${deptId}`,
method: 'GET',
loadingText: false
});
}
//销号申请 //销号申请
//申请销号 //申请销号
export function applyDelete(params) { export function applyDelete(params) {
@@ -245,6 +295,14 @@ export function getCheckTableDetail(params) {
}); });
} }
// 获取检查表详情(排查验证用,新流程)
export function getOneTableInspectDetail(id) {
return requestAPI({
url: `/admin/oneTable/detail/${id}`,
method: 'GET'
});
}
// 获取企业类型下拉列表 // 获取企业类型下拉列表
export function getEnterprisetype() { export function getEnterprisetype() {

View File

@@ -2,9 +2,11 @@ import Request from './luch-request/index.js';
// 基础的url // 基础的url
// const baseUrl = 'https://yingji.hexieapi.com/prod-api'; const baseUrl = 'https://yingji.hexieapi.com/prod-api';
const baseUrl = 'http://192.168.1.168:5004'; //廖哥本地 // const baseUrl = 'http://192.168.1.168:5004'; //廖哥本地
// const baseUrl = 'http://192.168.1.140:5004'; //超哥本地 // const baseUrl = 'http://192.168.1.140:5004'; //超哥本地
// const baseUrl = 'http://192.168.1.158:7003'; //测试环境
// 图片/文件资源域名:去掉 /prod-api便于 <image> / previewImage / downloadFile 直接访问 // 图片/文件资源域名:去掉 /prod-api便于 <image> / previewImage / downloadFile 直接访问
const imageBaseUrl = baseUrl.replace(/\/prod-api\/?$/, ''); const imageBaseUrl = baseUrl.replace(/\/prod-api\/?$/, '');

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

View File

@@ -4,6 +4,8 @@
"pages/map/map", "pages/map/map",
"pages/plandetail/plandetail", "pages/plandetail/plandetail",
"pages/Inspectionresult/Inspectionresult", "pages/Inspectionresult/Inspectionresult",
"pages/Inspectionresult/list",
"pages/Inspectionresult/detail",
"pages/membermanagemen/membermanagemen", "pages/membermanagemen/membermanagemen",
"pages/corporateInformation/corporateInformation", "pages/corporateInformation/corporateInformation",
"pages/editcompanInformation/editcompanInformation", "pages/editcompanInformation/editcompanInformation",
@@ -14,6 +16,7 @@
"pages/Idphotomanagement/Idphotomanagement", "pages/Idphotomanagement/Idphotomanagement",
"pages/hiddendanger/Inspection", "pages/hiddendanger/Inspection",
"pages/hiddendanger/view", "pages/hiddendanger/view",
"pages/hiddendanger/detail2",
"pages/hiddendanger/rectification", "pages/hiddendanger/rectification",
"pages/hiddendanger/acceptance", "pages/hiddendanger/acceptance",
"pages/hiddendanger/assignment", "pages/hiddendanger/assignment",

View File

@@ -1 +1 @@
"use strict";exports._imports_0="/static/home_icon/jianbianbeijing.png",exports._imports_0$1="/static/yujin/yujin_sousuo.png",exports._imports_0$2="/static/my/edit.png",exports._imports_0$3="/static/my/Customer service.png",exports._imports_0$4="/static/index/index_bg.png",exports._imports_0$5="/static/index/phone.png",exports._imports_0$6="/static/index/蒙版组 260.png",exports._imports_1="/static/yujin/yujin_tongji.png",exports._imports_1$1="/static/my/Notification.png",exports._imports_1$2="/static/my/Phone.png",exports._imports_1$3="/static/index/lock.png",exports._imports_2="/static/my/Account.png"; "use strict";exports._imports_0="/static/home_icon/jianbianbeijing.png",exports._imports_0$1="/static/yinhuan_detail/status.png",exports._imports_0$2="/static/yujin/yujin_sousuo.png",exports._imports_0$3="/static/my/edit.png",exports._imports_0$4="/static/my/Customer service.png",exports._imports_0$5="/static/index/index_bg.png",exports._imports_0$6="/static/index/phone.png",exports._imports_0$7="/static/index/蒙版组 260.png",exports._imports_1="/static/yinhuan_detail/date.png",exports._imports_1$1="/static/yujin/yujin_tongji.png",exports._imports_1$2="/static/my/Notification.png",exports._imports_1$3="/static/my/Phone.png",exports._imports_1$4="/static/index/lock.png",exports._imports_2="/static/my/Account.png";

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
{
"component": true,
"usingComponents": {
"up-upload": "../../uni_modules/uview-plus/components/u-upload/u-upload",
"up-input": "../../uni_modules/uview-plus/components/u-input/u-input",
"up-textarea": "../../uni_modules/uview-plus/components/u-textarea/u-textarea",
"u-popup": "../../uni_modules/uview-plus/components/u-popup/u-popup"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
"use strict";const t=require("../../utils/upload.js"),e=[{id:2,title:"一般隐患"},{id:3,title:"重大隐患"}],l=[{id:1,title:"部门检查"},{id:2,title:"督导检查"},{id:3,title:"企业自查"},{id:4,title:"行业互查"}];exports.AI_LEVEL_MAP={"轻微":0,"轻微隐患":0,"一般":0,"一般隐患":0,"重大":1,"重大隐患":1},exports.LEVEL_OPTIONS=e,exports.SOURCE_OPTIONS=l,exports.buildHiddenDangerParams=function(a,i={}){var r,d,n;const{formData:s,address:o,lng:u,lat:c,areaId:g,fileList:I}=a,p=null==(r=a.tagOptions)?void 0:r[s.tagIndex],f=s.tagId??(p?p.id:null);return{title:s.title,level:(null==(d=e[s.level])?void 0:d.id)||2,lng:u||0,lat:c||0,address:o||"",areaId:g||null,description:s.description||"",source:(null==(n=l[s.source])?void 0:n.title)||"",tagId:f,attachments:(I||[]).filter((t=>"success"===t.status)).map((e=>t.buildAttachmentItem(e))),regulationId:s.regulationId||null,...i}},exports.createEmptyHazardPayload=function(){return{formData:{title:"",level:0,source:0,description:"",tagIndex:0,tagId:null,regulationId:null,regulationName:""},address:"",lng:0,lat:0,areaId:"",areaName:"",fileList:[]}},exports.hasValidHazardPayload=function(t){if(!t)return!1;const{formData:e,fileList:l}=t;return!!((null==e?void 0:e.title)&&(null==l?void 0:l.length)>0)};

View File

@@ -0,0 +1 @@
"use strict";require("../../common/vendor.js"),require("../../request/request.js");

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.hazard-detail-panel-v2.data-v-1547c16c{flex:1;min-height:0;width:100%;height:100%;display:flex;flex-direction:column;overflow:hidden}.loading-wrap.data-v-1547c16c,.empty-wrap.data-v-1547c16c{flex:1;display:flex;align-items:center;justify-content:center;min-height:0}.loading-text.data-v-1547c16c,.empty-text.data-v-1547c16c{font-size:28rpx;color:#909399}.detail-body.data-v-1547c16c{display:flex;gap:20rpx;align-items:stretch;overflow:hidden;box-sizing:border-box}.steps-card.data-v-1547c16c{width:140rpx;flex-shrink:0;background:#fff;border-radius:20rpx;box-sizing:border-box}.step-item.data-v-1547c16c{padding:0 12rpx}.step-track.data-v-1547c16c{display:flex;flex-direction:column;align-items:center;padding:28rpx 0 0}.step-dot.data-v-1547c16c{width:66rpx;height:66rpx;border-radius:50%;background:#f3f3f3;display:flex;align-items:center;justify-content:center;transition:background .2s}.step-dot--active.data-v-1547c16c{background:#2667e9}.step-icon.data-v-1547c16c{width:36rpx;height:36rpx}.step-label.data-v-1547c16c{margin-top:12rpx;font-size:24rpx;color:#999;line-height:1.2}.step-label--active.data-v-1547c16c{color:#2667e9;font-weight:600}.step-line.data-v-1547c16c{width:0;height:40rpx;margin:10rpx 0;border-left:2rpx dashed #dcdfe6}.content-column.data-v-1547c16c{flex:1;width:0;min-height:0;box-sizing:border-box}.content-scroll.data-v-1547c16c{width:100%;box-sizing:border-box}.node-section.data-v-1547c16c{box-sizing:border-box;padding:0;margin-bottom:24rpx}.node-section--last.data-v-1547c16c{padding-bottom:40rpx;margin-bottom:0}.detail-card.data-v-1547c16c{background:#fff;border-radius:20rpx;overflow:hidden;padding:28rpx 38rpx;box-sizing:border-box}.card-header-v2.data-v-1547c16c{display:flex;align-items:flex-start;justify-content:space-between;gap:16rpx;padding:0}.card-header-main.data-v-1547c16c{flex:1;min-width:0}.operator.data-v-1547c16c{font-size:28rpx;font-weight:600;color:#303133;line-height:1.5;word-break:break-all}.time.data-v-1547c16c{display:block;margin-top:8rpx;font-size:24rpx;color:#999;line-height:1.4}.level-badge.data-v-1547c16c{flex-shrink:0;padding:6rpx 16rpx;border-radius:8rpx;font-size:22rpx;font-weight:500;white-space:nowrap}.level-badge.level-normal.data-v-1547c16c{background:#fff7e6;border:2rpx solid #ffd591;color:#fa8c16}.level-badge.level-major.data-v-1547c16c{background:#fff1f0;border:2rpx solid #ffa39e;color:#f5222d}.card-divider.data-v-1547c16c{height:0;margin:20rpx 0;border-top:2rpx dashed #eee}.card-body.data-v-1547c16c{padding:0}.detail-row-v2.data-v-1547c16c{display:flex;align-items:flex-start;justify-content:flex-start;gap:24rpx;padding:18rpx 0}.detail-row-v2--block.data-v-1547c16c{flex-wrap:wrap}.label.data-v-1547c16c{flex-shrink:0;font-size:26rpx;color:#999;line-height:1.5}.value.data-v-1547c16c{flex:1;min-width:0;font-size:26rpx;color:#333;line-height:1.6;text-align:left;word-break:break-all}.value--inline.data-v-1547c16c{display:flex;align-items:center}.area-dot.data-v-1547c16c{width:20rpx;height:20rpx;border-radius:50%;margin-right:12rpx;flex-shrink:0}.level-tag.data-v-1547c16c{display:inline-flex;align-items:center;padding:4rpx 16rpx;border-radius:8rpx;font-size:24rpx;font-weight:500;line-height:1.4;white-space:nowrap}.level-normal.data-v-1547c16c{background:#fff7e6;border:2rpx solid #ffd591;color:#fa8c16}.level-major.data-v-1547c16c{background:#fff1f0;border:2rpx solid #ffa39e;color:#f5222d}.tag-badge.data-v-1547c16c{display:inline-flex;align-items:center;padding:4rpx 16rpx;border-radius:8rpx;font-size:24rpx;background:#eef3ff;border:2rpx solid #aac5fc;color:#2667e9}.attachment-list.data-v-1547c16c{display:flex;flex-wrap:wrap;gap:16rpx;flex:1;min-width:0}.attachment-img.data-v-1547c16c{width:160rpx;height:160rpx;border-radius:12rpx;background:#f5f7fa}.sign-img.data-v-1547c16c{width:300rpx;height:160rpx;border:1rpx solid #e4e7ed;border-radius:8rpx;background:#fafafa;flex:1;min-width:0;max-width:100%}.file-link.data-v-1547c16c{display:inline-block;padding:12rpx 20rpx;background:#f5f7fa;border:1rpx solid #e4e7ed;border-radius:8rpx;color:#2667e9;font-size:24rpx}.result-tag.data-v-1547c16c{padding:6rpx 16rpx;border-radius:6rpx;font-size:24rpx}.result-tag--pass.data-v-1547c16c{background:#f0f9eb;color:#67c23a}.result-tag--fail.data-v-1547c16c{background:#fef0f0;color:#f56c6c}

View File

@@ -0,0 +1 @@
"use strict";const e="submit",t="assign",a="rectify",r="verify",i="writeoff",s={1:"status-blue",2:"status-orange",3:"status-red",4:"status-yellow",5:"status-green"},n={"待交办":"status-blue","待整改":"status-orange","整改中":"status-orange","待验收":"status-red","待销号":"status-yellow","已完成":"status-green","已销号":"status-green"},o={2:"一般隐患",3:"重大隐患"};function m(e,t="-"){if(Array.isArray(e)&&e.length)return e.join("、");if("string"==typeof e&&e.trim()){const t=e.split(/[,,、]/).map((e=>e.trim())).filter(Boolean);return t.length?t.join("、"):e}return t}function c(e,t,a){(t||[]).forEach((t=>{if(!t)return;const s=t.verifyId??t.id;if(null!=s){if(a.has(s))return;a.add(s)}e.push(function(e){return!!e&&(2===e.type||"销号"===e.typeName)}(t)?function(e){return{type:i,nodeName:"销号",titlePrefix:"销号",operator:e.verifierName||"-",time:e.verifyTime||"-",content:{resultName:e.resultName||"-",remark:e.remark||"",attachments:e.attachments||[],signPath:e.signPath||"",writeoffDeptName:e.writeoffDeptName||"-"}}}(t):function(e){return{type:r,nodeName:"验收",titlePrefix:"验收",operator:e.verifierName||"-",time:e.verifyTime||"-",content:{resultName:e.resultName||"-",remark:e.remark||"",attachments:e.attachments||[],signPath:e.signPath||"",verifyDeptName:e.verifyDeptName||"-"}}}(t))}))}exports.generateHazardHistory=function(r){if(!r)return[];const i=[];var s;return i.push({type:e,nodeName:"提交",titlePrefix:"提交",operator:r.reporterName||"-",time:r.createdAt||"-",content:{title:r.title||"-",source:r.source||"-",areaName:r.areaName||"-",areaColor:r.areaColor||"",address:r.address||"-",level:r.level,levelName:r.levelName||(s=r.level,o[s]||"未知"),tagName:r.tagName||"-",description:r.description||"-",attachments:r.attachments||[],legalBasis:r.legalBasis||r.regulationName||"-",reporterPhone:r.reporterPhone||"-",reportDeptName:r.reportDeptName||"-"}}),r.assignFlows&&r.assignFlows.length>0&&r.assignFlows.forEach((e=>{var r;e.assignId&&i.push({type:t,nodeName:"交办",titlePrefix:"交办",operator:e.assignerName||"-",time:e.assignTime||"-",content:{assigneeName:e.assigneeName||"-",deadline:e.deadline||"-",assignDeptName:e.assignDeptName||"-",assignRemark:e.assignRemark||"-",priorityName:e.priorityName||"-"}}),e.rectify&&i.push({type:a,nodeName:"整改",titlePrefix:"整改",operator:e.rectify.rectifierName||"-",time:e.rectify.rectifyTime||"-",content:{rectifyPlan:e.rectify.rectifyPlan||"-",rectifyResult:e.rectify.rectifyResult||"-",rectificationMeasures:e.rectify.rectificationMeasures||"-",controlMeasures:e.rectify.controlMeasures||"-",deadline:e.deadline||"-",rectifierNames:(r=e.rectify,r?Array.isArray(r.rectifierNames)&&r.rectifierNames.length?r.rectifierNames.join("、"):Array.isArray(r.memberNames)&&r.memberNames.length?r.memberNames.join("、"):m(r.rectifierName,"-"):"-"),managerNames:m(e.rectify.managerNames,"-"),planCost:e.rectify.planCost,actualCost:e.rectify.actualCost,attachments:e.rectify.attachments||[],signPath:e.rectify.signPath||"",rectifyStatusName:e.rectify.rectifyStatusName||"-"}});const s=new Set;c(i,e.verifies||[],s),c(i,function(e){return e.writeOffs||e.writeoffs||[]}(e),s)})),i},exports.getStatusClass=function(e,t){return t&&n[t]?n[t]:s[e]||""},exports.getStepIconPath=function(e,t=!1){const a={submit:"tijiao",assign:"jiaoban",rectify:"zhenggai",verify:"yanshou",writeoff:"xiaohao"}[e]||"tijiao";return"zhenggai"===a?t?"/static/yinhuan_detail/zhenggai_selected.png":"/static/yinhuan_detail/zhenggai__unselected.png":`/static/yinhuan_detail/${a}__${t?"selected":"unselected"}.png`};

View File

@@ -0,0 +1 @@
"use strict";const e=require("../../common/vendor.js"),l=require("./hazardDetail.js");exports.useHazardDetailScroll=function(t,o){const n=e.getCurrentInstance(),u=(null==n?void 0:n.proxy)||n,r=e.ref([]),a=e.ref(0),i=e.ref([0]),c=e.ref(0),s=e.ref(""),v=e.ref(""),d=e.ref(!0),f=e.ref(!1);let h=null;const g=()=>{clearTimeout(h),h=setTimeout((()=>{r.value.length&&e.nextTick$1((()=>{const l=e.index.createSelectorQuery().in(u);l.select(".content-scroll").boundingClientRect(),l.select(".content-scroll").scrollOffset(),l.selectAll(".node-section").boundingClientRect(),l.exec((e=>{const l=null==e?void 0:e[0],t=null==e?void 0:e[1],o=(null==e?void 0:e[2])||[];if(!l||!o.length)return;c.value=l.height||0;const n=(null==t?void 0:t.scrollTop)||0;i.value=o.map((e=>e.top-l.top+n))}))}))}),80)};return e.watch((()=>"function"==typeof o?o():null==o?void 0:o.value),(e=>{e||(g(),setTimeout(g,300))})),e.watch((()=>"function"==typeof t?t():null==t?void 0:t.value),(e=>{return t=e,r.value=l.generateHazardHistory(t),a.value=0,s.value="",g(),void setTimeout(g,300);var t}),{immediate:!0,deep:!0}),e.watch(a,(e=>{v.value="hazard-step-"+e,setTimeout((()=>{v.value=""}),300)})),e.watch((()=>r.value.length),(()=>g())),e.onReady((()=>{g(),setTimeout(g,300)})),{historyList:r,activeIndex:a,contentScrollIntoView:s,stepScrollIntoView:v,scrollWithAnimation:d,onContentScroll:e=>{if(f.value)return;const{scrollTop:l=0,scrollHeight:t=0}=e.detail||{};((e,l=0)=>{const t=r.value.length;if(!t)return;if(l>0&&c.value>0&&e+c.value>=l-60)return void(a.value!==t-1&&(a.value=t-1));const o=i.value;if(!o.length)return;let n=0;for(let u=o.length-1;u>=0;u--)if(e>=o[u]-80){n=u;break}n!==a.value&&(a.value=n)})(l,t),g()},onScrollToLower:()=>{const e=r.value.length;e>0&&a.value!==e-1&&(a.value=e-1)},scrollToNode:e=>{e<0||e>=r.value.length||(f.value=!0,d.value=!0,a.value=e,s.value="hazard-node-"+e,setTimeout((()=>{s.value="",f.value=!1,g()}),350))},scheduleMeasureLayout:g}};

File diff suppressed because one or more lines are too long

View File

@@ -4,9 +4,6 @@
"u-radio": "../../uni_modules/uview-plus/components/u-radio/u-radio", "u-radio": "../../uni_modules/uview-plus/components/u-radio/u-radio",
"u-radio-group": "../../uni_modules/uview-plus/components/u-radio-group/u-radio-group", "u-radio-group": "../../uni_modules/uview-plus/components/u-radio-group/u-radio-group",
"up-textarea": "../../uni_modules/uview-plus/components/u-textarea/u-textarea", "up-textarea": "../../uni_modules/uview-plus/components/u-textarea/u-textarea",
"up-choose": "../../uni_modules/uview-plus/components/u-choose/u-choose", "hazard-form-popup": "../../components/hazard/HazardFormPopup"
"up-upload": "../../uni_modules/uview-plus/components/u-upload/u-upload",
"up-input": "../../uni_modules/uview-plus/components/u-input/u-input",
"u-popup": "../../uni_modules/uview-plus/components/u-popup/u-popup"
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
{
"navigationBarTitleText": "排查详情",
"usingComponents": {
"up-icon": "../../uni_modules/uview-plus/components/u-icon/u-icon",
"u-radio": "../../uni_modules/uview-plus/components/u-radio/u-radio",
"u-radio-group": "../../uni_modules/uview-plus/components/u-radio-group/u-radio-group",
"up-textarea": "../../uni_modules/uview-plus/components/u-textarea/u-textarea",
"u-popup": "../../uni_modules/uview-plus/components/u-popup/u-popup",
"hazard-form-popup": "../../components/hazard/HazardFormPopup"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
"use strict";const e=require("../../common/vendor.js"),a=require("../../request/api.js");if(!Array){e.resolveComponent("u-navbar")()}Math;const t={__name:"list",setup(t){const n=e.ref(!0),o=e.ref([]),l=e.ref(""),s=e.ref(""),r={pageNum:1,pageSize:100,name:""},i=e=>e?String(e).split(" ")[0]:"",u=e=>{const a=Number(e);return Number.isNaN(a)?0:Math.min(100,Math.max(0,a))};e.onLoad((e=>{e.tableId&&(l.value=e.tableId),e.name&&(s.value=decodeURIComponent(e.name))})),e.onShow((()=>{(async()=>{var t;if(!l.value)return n.value=!1,void(o.value=[]);n.value=!0;try{const s=await a.getPlanTableDetail({...r,tableId:l.value});0===s.code?o.value=(null==(t=s.data)?void 0:t.records)||[]:e.index.showToast({title:s.msg||"获取检查清单失败",icon:"none"})}catch(s){console.error("获取检查清单失败:",s),e.index.showToast({title:"获取检查清单失败",icon:"none"})}finally{n.value=!1}})()}));const d=()=>{e.index.switchTab({url:"/pages/index/index"})};return e.onBackPress((()=>(d(),!0))),(a,t)=>e.e({a:e.o(d),b:e.p({title:"检查列表",placeholder:!0,safeAreaInsetTop:!0,bgColor:"#3375e6",titleColor:"#ffffff",leftIconColor:"#ffffff",autoBack:!1}),c:n.value},n.value?{}:l.value?0===o.value.length?{}:{f:e.f(o.value,((a,t,n)=>e.e({a:e.t(a.name||s.value||"检查任务"),b:e.t(a.runModeName),c:e.t(a.cycle),d:a.taskDate},a.taskDate?{e:e.t(i(a.taskDate))}:{},{f:e.t(i(a.planStartTime)),g:e.t(i(a.planEndTime)),h:u(a.progress)+"%",i:e.t(u(a.progress)),j:e.t(a.totalCount??0),k:e.t(a.pendingCount??0),l:e.t(a.unusualNum??0),m:e.t(a.finishedCount??0),n:e.o((t=>(a=>{const t=a.id,n=i(a.taskDate);if(!t||!n)return void e.index.showToast({title:"缺少任务参数",icon:"none"});const o=a.name||s.value||"",r=l.value?`&tableId=${l.value}`:"";e.index.navigateTo({url:`/pages/Inspectionresult/detail?oneTableId=${t}&taskDate=${encodeURIComponent(n)}&name=${encodeURIComponent(o)}${r}`})})(a)),a.id||a.taskDate),o:a.id||a.taskDate})))}:{},{d:!l.value,e:0===o.value.length,g:e.gei(a,"")})}},n=e._export_sfc(t,[["__scopeId","data-v-ea20e208"]]);wx.createPage(n);

View File

@@ -0,0 +1,7 @@
{
"navigationStyle": "custom",
"navigationBarTitleText": "检查列表",
"usingComponents": {
"u-navbar": "../../uni_modules/uview-plus/components/u-navbar/u-navbar"
}
}

View File

@@ -0,0 +1 @@
<view class="{{['page', 'data-v-ea20e208', virtualHostClass]}}" style="{{virtualHostStyle}}" hidden="{{virtualHostHidden || false}}" id="{{g}}"><u-navbar wx:if="{{b}}" class="data-v-ea20e208" virtualHostClass="data-v-ea20e208" bindleftClick="{{a}}" u-i="ea20e208-0" bind:__l="__l" u-p="{{b}}"/><view class="page-content padding data-v-ea20e208"><view wx:if="{{c}}" class="empty-tip text-gray text-center padding data-v-ea20e208">加载中...</view><view wx:elif="{{d}}" class="empty-tip text-gray text-center padding data-v-ea20e208"> 请从首页检查计划进入 </view><view wx:elif="{{e}}" class="empty-tip text-gray text-center padding data-v-ea20e208"> 暂无检查任务 </view><block wx:else><view wx:for="{{f}}" wx:for-item="item" wx:key="o" class="plan-card margin-bottom data-v-ea20e208"><view class="plan-header data-v-ea20e208"><text class="plan-header-title data-v-ea20e208">{{item.a}}</text></view><view class="plan-body data-v-ea20e208"><view class="flex data-v-ea20e208"><view class="border-border margin-right-xs data-v-ea20e208">{{item.b}}完成</view><view class="border-border data-v-ea20e208">{{item.c}}</view></view><view wx:if="{{item.d}}" class="flex text-gray margin-top data-v-ea20e208"><view class="data-v-ea20e208">任务日期:</view><view class="data-v-ea20e208" style="color:#333333">{{item.e}}</view></view><view class="flex text-gray margin-top data-v-ea20e208"><view class="data-v-ea20e208">计划时间:</view><view class="data-v-ea20e208" style="color:#333333">{{item.f}}至{{item.g}}</view></view><view class="flex margin-top align-center data-v-ea20e208"><view class="data-v-ea20e208" style="color:#B5B5B5">完成进度:</view><view class="flex align-center margin-left-sm data-v-ea20e208"><view class="cu-progress round data-v-ea20e208"><view class="bg-green data-v-ea20e208" style="{{'width:' + item.h}}"></view></view><text class="margin-left-sm data-v-ea20e208">{{item.i}}%</text></view></view><view class="plan-stats margin-top data-v-ea20e208"><view class="plan-stat-item data-v-ea20e208"><view class="plan-stat-num text-orange data-v-ea20e208">{{item.j}}</view><view class="plan-stat-label data-v-ea20e208">总检项</view></view><view class="plan-stat-item data-v-ea20e208"><view class="plan-stat-num text-yellow data-v-ea20e208">{{item.k}}</view><view class="plan-stat-label data-v-ea20e208">待排查</view></view><view class="plan-stat-item data-v-ea20e208"><view class="plan-stat-num text-red data-v-ea20e208">{{item.l}}</view><view class="plan-stat-label data-v-ea20e208">异常数</view></view><view class="plan-stat-item data-v-ea20e208"><view class="plan-stat-num text-blue data-v-ea20e208">{{item.m}}</view><view class="plan-stat-label data-v-ea20e208">已完成</view></view></view><view class="margin-top margin-bottom flex justify-end data-v-ea20e208"><button class="cu-btn round lg bg-blue data-v-ea20e208" catchtap="{{item.n}}"> 检查处置 </button></view></view></view></block></view></view>

View File

@@ -0,0 +1 @@
.page.data-v-ea20e208{min-height:100vh;background:#ebf2fc}.empty-tip.data-v-ea20e208{font-size:28rpx}.plan-card.data-v-ea20e208{border-radius:16rpx;overflow:hidden;box-shadow:0 2rpx 6rpx 2rpx rgba(0,0,0,.06)}.plan-header.data-v-ea20e208{background:linear-gradient(135deg,#4a90e2,#2667e9);padding:24rpx 30rpx;display:flex;align-items:center}.plan-header .plan-header-title.data-v-ea20e208{color:#fff;font-size:30rpx;font-weight:700}.plan-body.data-v-ea20e208{padding:24rpx 30rpx 10rpx;background:#fff}.plan-stats.data-v-ea20e208{display:flex;background:#f5f7fa;border-radius:12rpx;border:1rpx solid #E8ECF0;overflow:hidden}.plan-stats .plan-stat-item.data-v-ea20e208{flex:1;text-align:center;padding:20rpx 0;border-right:1rpx solid #E8ECF0}.plan-stats .plan-stat-item.data-v-ea20e208:last-child{border-right:none}.plan-stats .plan-stat-num.data-v-ea20e208{font-size:36rpx;font-weight:700}.plan-stats .plan-stat-label.data-v-ea20e208{font-size:24rpx;color:#666;margin-top:8rpx}.cu-progress.data-v-ea20e208{width:300rpx;height:20rpx;background:#ebeef5;border-radius:100rpx;overflow:hidden}.cu-progress view.data-v-ea20e208{height:100%;border-radius:100rpx;transition:width .3s ease}.bg-green.data-v-ea20e208{background:#2667e9}.border-border.data-v-ea20e208{padding:10rpx;background:#eef3ff;border-radius:4rpx;border:2rpx solid #AAC5FC;text-align:center;display:flex;justify-content:center;align-items:center;font-size:28rpx;color:#2667e9}

View File

@@ -1 +1 @@
"use strict";const e=require("../../common/vendor.js"),t=require("../../common/assets.js"),a=require("../../request/api.js");if(!Array){(e.resolveComponent("up-datetime-picker")+e.resolveComponent("up-input")+e.resolveComponent("u-loadmore"))()}Math||((()=>"../../uni_modules/uview-plus/components/u-datetime-picker/u-datetime-picker.js")+(()=>"../../uni_modules/uview-plus/components/u-input/u-input.js")+(()=>"../../uni_modules/uview-plus/components/u-loadmore/u-loadmore.js"))();const o={__name:"Inspectionwarning",setup(o){const l=e.reactive({startDate:"",endDate:"",deptName:""}),u=e.ref(!1),s=e.ref(!1),n=e.ref(Number(new Date)),r=e.ref(Number(new Date)),d=e.reactive({total:0,overdue:0,pending:0,completed:0,overdueCompleted:0,onTimeCompleted:0});e.reactive({total:0,overdue:0,completed:0,pending:0});const i=e.ref([]),v=e.ref(1),c=e.ref(20),m=e.ref("loadmore"),p=e.ref([{label:"全部状态",value:0,count:null},{label:"逾期未检",value:1,count:null},{label:"严重逾期",value:2,count:null},{label:"期限内待检",value:3,count:null},{label:"逾期已完成",value:4,count:null},{label:"按期已完成",value:5,count:null}]),g=e.ref(0),f=e=>{const t=new Date(e);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`},D=e=>{const t=f(e.value);l.startDate=t,u.value=!1},h=e=>{const t=f(e.value);l.endDate=t,s.value=!1},N=e=>{if(!e||"按期"===e)return"status-normal";const t=parseInt(e);return t>=7?"status-serious":t>=1?"status-overdue":"status-normal"},b=(e,t)=>{if(!e||"按期"===e)return"已完成"===t?"按期已完成":"期限内待检";const a=parseInt(e);return a>=7?"严重逾期":a>=1?"已完成"===t?"逾期已完成":"逾期未检":"期限内待检"},w=async()=>{try{const e={pageNum:v.value,pageSize:c.value};l.startDate&&(e.startDate=l.startDate),l.endDate&&(e.endDate=l.endDate),l.deptName&&l.deptName.trim()&&(e.deptName=l.deptName.trim());const t=p.value[g.value].value;0!==t&&(e.inspectionStatus=t);const o=await a.getInspectionWarningList(e);if(0===o.code)if(o.data.statistics&&(d.total=o.data.statistics.total||0,d.overdue=o.data.statistics.overdue||0,d.pending=o.data.statistics.pending||0,d.completed=o.data.statistics.completed||0,d.overdueCompleted=o.data.statistics.overdueCompleted||0,d.onTimeCompleted=o.data.statistics.onTimeCompleted||0,p.value[0].count=o.data.statistics.total||0,p.value[1].count=o.data.statistics.overdue||0,p.value[2].count=o.data.statistics.pending||0,p.value[3].count=o.data.statistics.completed||0,p.value[4].count=o.data.statistics.overdueCompleted||0,p.value[5].count=o.data.statistics.onTimeCompleted||0),o.data.page&&o.data.page.records){const e=o.data.page.records;1===v.value?i.value=e:i.value=[...i.value,...e];const t=o.data.page.total||0;i.value.length>=t?m.value="nomore":m.value="loadmore"}else m.value="nomore"}catch(e){console.error("获取预警列表失败:",e)}},C=()=>{v.value=1,i.value=[],w()};return e.onReachBottom((()=>{"loadmore"===m.value&&(v.value++,w())})),e.onShow((()=>{w()})),(a,o)=>e.e({a:t._imports_0$1,b:e.t(l.startDate||"请选择"),c:e.n(l.startDate?"date-value":"date-placeholder"),d:e.o((e=>u.value=!0)),e:e.o(D),f:e.o((e=>u.value=!1)),g:e.o((e=>u.value=!1)),h:e.o((e=>n.value=e)),i:e.p({show:u.value,mode:"date",modelValue:n.value}),j:e.t(l.endDate||"请选择"),k:e.n(l.endDate?"date-value":"date-placeholder"),l:e.o((e=>s.value=!0)),m:e.o(h),n:e.o((e=>s.value=!1)),o:e.o((e=>s.value=!1)),p:e.o((e=>r.value=e)),q:e.p({show:s.value,mode:"date",modelValue:r.value}),r:e.o((e=>l.deptName=e)),s:e.p({placeholder:"请输入公司名称",border:"surround",modelValue:l.deptName}),t:e.o(C),v:t._imports_1,w:e.t(d.total),x:e.t(d.overdue),y:e.t(d.onTimeCompleted),z:e.t(d.completed),A:e.f(p.value,((t,a,o)=>e.e({a:e.t(t.label),b:e.t(null!=t.count?t.count:""),c:g.value===a},(g.value,{}),{d:a,e:g.value===a?1:"",f:e.o((e=>(e=>{g.value=e,v.value=1,i.value=[],w()})(a)),a)}))),B:e.f(i.value,((t,a,o)=>({a:e.t(t.deptName||"-"),b:e.t(b(t.overdueDays,t.statusName)),c:e.n(N(t.overdueDays)),d:e.t(t.planName||"-"),e:e.t(t.cycleName||"-"),f:e.t(t.taskDate||"-"),g:e.t(t.finishTime||"未完成"),h:e.t(t.executorName||"-"),i:e.t(t.overdueDays||"-"),j:t.id}))),C:i.value.length>0},i.value.length>0?{D:e.p({status:m.value})}:{},{E:0===i.value.length},(i.value.length,{}),{F:e.gei(a,"")})}},l=e._export_sfc(o,[["__scopeId","data-v-b713017f"]]);wx.createPage(l); "use strict";const e=require("../../common/vendor.js"),t=require("../../common/assets.js"),a=require("../../request/api.js");if(!Array){(e.resolveComponent("up-datetime-picker")+e.resolveComponent("up-input")+e.resolveComponent("u-loadmore"))()}Math||((()=>"../../uni_modules/uview-plus/components/u-datetime-picker/u-datetime-picker.js")+(()=>"../../uni_modules/uview-plus/components/u-input/u-input.js")+(()=>"../../uni_modules/uview-plus/components/u-loadmore/u-loadmore.js"))();const o={__name:"Inspectionwarning",setup(o){const l=e.reactive({startDate:"",endDate:"",deptName:""}),u=e.ref(!1),s=e.ref(!1),n=e.ref(Number(new Date)),r=e.ref(Number(new Date)),d=e.reactive({total:0,overdue:0,pending:0,completed:0,overdueCompleted:0,onTimeCompleted:0});e.reactive({total:0,overdue:0,completed:0,pending:0});const i=e.ref([]),v=e.ref(1),c=e.ref(20),m=e.ref("loadmore"),p=e.ref([{label:"全部状态",value:0,count:null},{label:"逾期未检",value:1,count:null},{label:"严重逾期",value:2,count:null},{label:"期限内待检",value:3,count:null},{label:"逾期已完成",value:4,count:null},{label:"按期已完成",value:5,count:null}]),g=e.ref(0),f=e=>{const t=new Date(e);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`},D=e=>{const t=f(e.value);l.startDate=t,u.value=!1},h=e=>{const t=f(e.value);l.endDate=t,s.value=!1},N=e=>{if(!e||"按期"===e)return"status-normal";const t=parseInt(e);return t>=7?"status-serious":t>=1?"status-overdue":"status-normal"},b=(e,t)=>{if(!e||"按期"===e)return"已完成"===t?"按期已完成":"期限内待检";const a=parseInt(e);return a>=7?"严重逾期":a>=1?"已完成"===t?"逾期已完成":"逾期未检":"期限内待检"},w=async()=>{try{const e={pageNum:v.value,pageSize:c.value};l.startDate&&(e.startDate=l.startDate),l.endDate&&(e.endDate=l.endDate),l.deptName&&l.deptName.trim()&&(e.deptName=l.deptName.trim());const t=p.value[g.value].value;0!==t&&(e.inspectionStatus=t);const o=await a.getInspectionWarningList(e);if(0===o.code)if(o.data.statistics&&(d.total=o.data.statistics.total||0,d.overdue=o.data.statistics.overdue||0,d.pending=o.data.statistics.pending||0,d.completed=o.data.statistics.completed||0,d.overdueCompleted=o.data.statistics.overdueCompleted||0,d.onTimeCompleted=o.data.statistics.onTimeCompleted||0,p.value[0].count=o.data.statistics.total||0,p.value[1].count=o.data.statistics.overdue||0,p.value[2].count=o.data.statistics.pending||0,p.value[3].count=o.data.statistics.completed||0,p.value[4].count=o.data.statistics.overdueCompleted||0,p.value[5].count=o.data.statistics.onTimeCompleted||0),o.data.page&&o.data.page.records){const e=o.data.page.records;1===v.value?i.value=e:i.value=[...i.value,...e];const t=o.data.page.total||0;i.value.length>=t?m.value="nomore":m.value="loadmore"}else m.value="nomore"}catch(e){console.error("获取预警列表失败:",e)}},C=()=>{v.value=1,i.value=[],w()};return e.onReachBottom((()=>{"loadmore"===m.value&&(v.value++,w())})),e.onShow((()=>{w()})),(a,o)=>e.e({a:t._imports_0$2,b:e.t(l.startDate||"请选择"),c:e.n(l.startDate?"date-value":"date-placeholder"),d:e.o((e=>u.value=!0)),e:e.o(D),f:e.o((e=>u.value=!1)),g:e.o((e=>u.value=!1)),h:e.o((e=>n.value=e)),i:e.p({show:u.value,mode:"date",modelValue:n.value}),j:e.t(l.endDate||"请选择"),k:e.n(l.endDate?"date-value":"date-placeholder"),l:e.o((e=>s.value=!0)),m:e.o(h),n:e.o((e=>s.value=!1)),o:e.o((e=>s.value=!1)),p:e.o((e=>r.value=e)),q:e.p({show:s.value,mode:"date",modelValue:r.value}),r:e.o((e=>l.deptName=e)),s:e.p({placeholder:"请输入公司名称",border:"surround",modelValue:l.deptName}),t:e.o(C),v:t._imports_1$1,w:e.t(d.total),x:e.t(d.overdue),y:e.t(d.onTimeCompleted),z:e.t(d.completed),A:e.f(p.value,((t,a,o)=>e.e({a:e.t(t.label),b:e.t(null!=t.count?t.count:""),c:g.value===a},(g.value,{}),{d:a,e:g.value===a?1:"",f:e.o((e=>(e=>{g.value=e,v.value=1,i.value=[],w()})(a)),a)}))),B:e.f(i.value,((t,a,o)=>({a:e.t(t.deptName||"-"),b:e.t(b(t.overdueDays,t.statusName)),c:e.n(N(t.overdueDays)),d:e.t(t.planName||"-"),e:e.t(t.cycleName||"-"),f:e.t(t.taskDate||"-"),g:e.t(t.finishTime||"未完成"),h:e.t(t.executorName||"-"),i:e.t(t.overdueDays||"-"),j:t.id}))),C:i.value.length>0},i.value.length>0?{D:e.p({status:m.value})}:{},{E:0===i.value.length},(i.value.length,{}),{F:e.gei(a,"")})}},l=e._export_sfc(o,[["__scopeId","data-v-b713017f"]]);wx.createPage(l);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,6 @@
{ {
"navigationBarTitleText": "隐患排查", "navigationBarTitleText": "隐患排查",
"usingComponents": { "usingComponents": {
"up-choose": "../../uni_modules/uview-plus/components/u-choose/u-choose", "hazard-form-popup": "../../components/hazard/HazardFormPopup"
"up-upload": "../../uni_modules/uview-plus/components/u-upload/u-upload",
"up-input": "../../uni_modules/uview-plus/components/u-input/u-input",
"up-textarea": "../../uni_modules/uview-plus/components/u-textarea/u-textarea",
"u-popup": "../../uni_modules/uview-plus/components/u-popup/u-popup"
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
.page.data-v-df836b84{min-height:100vh;background:#ebf2fc}.result-btn.data-v-df836b84{flex:1;height:80rpx;line-height:80rpx;border-radius:8rpx;background:#f5f5f5;color:#666;font-size:28rpx}.result-btn.data-v-df836b84:after{border:none}.result-btn.active.data-v-df836b84{background:#2667e9;color:#fff}.signature-box.data-v-df836b84{width:100%;min-height:240rpx;background:#f8f8f8;border:1rpx dashed #dcdfe6;border-radius:8rpx;margin-top:16rpx}.signature-box .signature-img.data-v-df836b84{width:100%;height:100%}.signature-box .signature-placeholder.data-v-df836b84{color:#909399;font-size:28rpx} .page.data-v-fa0c6117{min-height:100vh;background:#ebf2fc}.result-btn.data-v-fa0c6117{flex:1;height:80rpx;line-height:80rpx;border-radius:8rpx;background:#f5f5f5;color:#666;font-size:28rpx}.result-btn.data-v-fa0c6117:after{border:none}.result-btn.active.data-v-fa0c6117{background:#2667e9;color:#fff}.signature-box.data-v-fa0c6117{width:100%;min-height:240rpx;background:#f8f8f8;border:1rpx dashed #dcdfe6;border-radius:8rpx;margin-top:16rpx}.signature-box .signature-img.data-v-fa0c6117{width:100%;height:100%}.signature-box .signature-placeholder.data-v-fa0c6117{color:#909399;font-size:28rpx}

View File

@@ -1 +1 @@
"use strict";const e=require("../../common/vendor.js"),a=require("../../request/api.js");if(!Array){(e.resolveComponent("u-popup")+e.resolveComponent("up-datetime-picker"))()}Math||((()=>"../../uni_modules/uview-plus/components/u-popup/u-popup.js")+(()=>"../../uni_modules/uview-plus/components/u-datetime-picker/u-datetime-picker.js"))();const t={__name:"assignment",setup(t){const n=e.ref(""),u=e.ref(""),o=e.ref(!1),r=e.ref(""),l=e.ref(""),s=e.ref([]),i=e.ref(0),v=e.ref(""),d=e=>e.postName?`${e.nickName}_${e.postName}`:e.nickName||"",c=e.computed((()=>{const e=s.value[i.value];return(null==e?void 0:e.users)||[]})),g=e.computed((()=>{if(!v.value)return"";for(const e of s.value){const a=(e.users||[]).find((e=>String(e.userId)===String(v.value)));if(a)return d(a)}return""})),p=e=>{var a;return!(!v.value||!(null==(a=e.users)?void 0:a.length))&&e.users.some((e=>String(e.userId)===String(v.value)))},m=()=>{v.value=l.value;const e=s.value.findIndex((e=>{var a;return(null==(a=e.users)?void 0:a.length)>0}));i.value=e>=0?e:0,o.value=!0},f=()=>{o.value=!1},S=()=>{v.value?(l.value=String(v.value),r.value=g.value,o.value=!1):e.index.showToast({title:"请选择整改责任人",icon:"none"})},h=e.ref(!1),w=e.ref(Date.now()),x=e.ref(""),I=e=>{console.log("选择的日期时间:",e);const a=new Date(e.value),t=a.getFullYear(),n=String(a.getMonth()+1).padStart(2,"0"),u=String(a.getDate()).padStart(2,"0"),o=String(a.getHours()).padStart(2,"0"),r=String(a.getMinutes()).padStart(2,"0"),l=String(a.getSeconds()).padStart(2,"0");x.value=`${t}-${n}-${u} ${o}:${r}:${l}`,h.value=!1},y=()=>{e.index.navigateBack()},k=async()=>{if(!l.value)return void e.index.showToast({title:"请选择整改人员",icon:"none"});if(!x.value)return void e.index.showToast({title:"请选择整改期限",icon:"none"});const t={hazardId:Number(n.value),assigneeId:Number(l.value),deadline:x.value,assignRemark:""};console.log("提交数据:",t);try{const n=await a.assignHiddenDanger(t);0===n.code?(N(!1),e.index.showToast({title:"交办成功",icon:"success"}),setTimeout((()=>{e.index.navigateBack()}),1500)):e.index.showToast({title:n.msg||"交办失败",icon:"none"})}catch(u){console.error("交办失败:",u),e.index.showToast({title:"请求失败",icon:"none"})}},T=e.ref(!1),$=e.ref(!1),_=e.ref(!1),D=()=>`draft_assign_${n.value||""}`,N=(a=!0)=>{const t=D();e.index.removeStorageSync(t),T.value=!1,$.value=!1,_.value=!0,x.value="",w.value=Date.now(),e.nextTick$1((()=>{_.value=!1})),a&&e.index.showToast({title:"草稿已清空",icon:"none"})};return e.watch((()=>[x.value]),(()=>{n.value&&(()=>{if(_.value)return;const a=D();if(!x.value)return e.index.removeStorageSync(a),void(T.value=!1);const t={selectedDate:x.value,dateValue:w.value};e.index.setStorageSync(a,JSON.stringify(t)),T.value=!0})()})),e.onLoad((t=>{t.hazardId&&(n.value=t.hazardId),t.assignId&&(u.value=t.assignId),(async()=>{try{const e=await a.getDepartmentPersonUsers();0===e.code&&e.data&&(s.value=e.data,console.log("部门人员树:",s.value))}catch(e){console.error("获取部门人员失败:",e)}})(),(()=>{const a=D(),t=e.index.getStorageSync(a);if(t)try{const a=JSON.parse(t);if(!a.selectedDate)return;_.value=!0,x.value=a.selectedDate||"",w.value=a.dateValue||Date.now(),T.value=!0,$.value=!0,e.nextTick$1((()=>{_.value=!1})),e.index.showToast({title:"已自动恢复您上次未提交的内容",icon:"none",duration:2500})}catch(n){console.error("解析草稿失败:",n),_.value=!1}})()})),(a,t)=>e.e({a:$.value},$.value?{b:e.o((e=>N(!0)))}:{},{c:e.t(r.value||"请选择整改责任人"),d:r.value?"":1,e:e.o(m),f:e.o(f),g:v.value},v.value?{h:e.t(g.value)}:{},{i:e.f(s.value,((a,t,n)=>e.e({a:e.t(a.deptName),b:p(a)},(p(a),{}),{c:a.deptId,d:e.n({active:i.value===t}),e:e.o((e=>i.value=t),a.deptId)}))),j:0===c.value.length},0===c.value.length?{}:{k:e.f(c.value,((a,t,n)=>e.e({a:e.t(d(a)),b:String(v.value)===String(a.userId)},(String(v.value),String(a.userId),{}),{c:"user-"+a.userId,d:String(v.value)===String(a.userId)?1:"",e:e.o((e=>{return t=a.userId,void(v.value=String(t));var t}),"user-"+a.userId)})))},{l:"dept-users-"+i.value,m:e.o(f),n:e.o(S),o:e.o(f),p:e.p({show:o.value,mode:"bottom",round:"20"}),q:e.t(x.value||"请选择整改期限"),r:e.n(x.value?"":"text-gray"),s:e.o((e=>h.value=!0)),t:e.o(I),v:e.o((e=>h.value=!1)),w:e.o((e=>h.value=!1)),x:e.o((e=>w.value=e)),y:e.p({show:h.value,mode:"datetime",modelValue:w.value}),z:e.o(y),A:e.o(k),B:e.gei(a,"")})}},n=e._export_sfc(t,[["__scopeId","data-v-860f0a5c"]]);wx.createPage(n); "use strict";const e=require("../../common/vendor.js"),a=require("../../request/api.js"),t=require("../../utils/draftCache.js"),u=require("../../utils/useDraftCache.js");if(!Array){(e.resolveComponent("u-popup")+e.resolveComponent("up-datetime-picker"))()}Math||((()=>"../../uni_modules/uview-plus/components/u-popup/u-popup.js")+(()=>"../../uni_modules/uview-plus/components/u-datetime-picker/u-datetime-picker.js"))();const n={__name:"assignment",setup(n){const r=e.ref(""),o=e.ref(""),l=e.ref(!1),s=e.ref(""),i=e.ref(""),v=e.ref([]),d=e.ref(0),c=e.ref(""),g=e=>e.postName?`${e.nickName}_${e.postName}`:e.nickName||"",p=e.computed((()=>{const e=v.value[d.value];return(null==e?void 0:e.users)||[]})),m=e.computed((()=>{if(!c.value)return"";for(const e of v.value){const a=(e.users||[]).find((e=>String(e.userId)===String(c.value)));if(a)return g(a)}return""})),f=e=>{var a;return!(!c.value||!(null==(a=e.users)?void 0:a.length))&&e.users.some((e=>String(e.userId)===String(c.value)))},S=()=>{c.value=i.value;const e=v.value.findIndex((e=>{var a;return(null==(a=e.users)?void 0:a.length)>0}));d.value=e>=0?e:0,l.value=!0},h=()=>{l.value=!1},w=()=>{c.value?(i.value=String(c.value),s.value=m.value,l.value=!1):e.index.showToast({title:"请选择整改责任人",icon:"none"})},I=e.ref(!1),D=e.ref(Date.now()),x=e.ref(""),{showRestoreBanner:y,clear:_,restore:k,bindAutoSave:N}=u.useDraftCache({getKey:()=>t.buildDraftKey(t.DRAFT_NS.ASSIGN,r.value),getPayload:()=>({selectedDate:x.value,dateValue:D.value}),hasContent:e=>!!e.selectedDate,applyPayload:e=>{x.value=e.selectedDate||"",D.value=e.dateValue||Date.now()},clearForm:()=>{x.value="",D.value=Date.now()},canSave:()=>!!r.value});N((()=>[x.value]));const b=e=>{console.log("选择的日期时间:",e);const a=new Date(e.value),t=a.getFullYear(),u=String(a.getMonth()+1).padStart(2,"0"),n=String(a.getDate()).padStart(2,"0"),r=String(a.getHours()).padStart(2,"0"),o=String(a.getMinutes()).padStart(2,"0"),l=String(a.getSeconds()).padStart(2,"0");x.value=`${t}-${u}-${n} ${r}:${o}:${l}`,I.value=!1},T=()=>{e.index.navigateBack()},$=async()=>{if(!i.value)return void e.index.showToast({title:"请选择整改人员",icon:"none"});if(!x.value)return void e.index.showToast({title:"请选择整改期限",icon:"none"});const t={hazardId:Number(r.value),assigneeId:Number(i.value),deadline:x.value,assignRemark:""};console.log("提交数据:",t);try{const u=await a.assignHiddenDanger(t);0===u.code?(_(!1),e.index.showToast({title:"交办成功",icon:"success"}),setTimeout((()=>{e.index.navigateBack()}),1500)):e.index.showToast({title:u.msg||"交办失败",icon:"none"})}catch(u){console.error("交办失败:",u),e.index.showToast({title:"请求失败",icon:"none"})}};return e.onLoad((e=>{e.hazardId&&(r.value=e.hazardId),e.assignId&&(o.value=e.assignId),(async()=>{try{const e=await a.getDepartmentPersonUsers();0===e.code&&e.data&&(v.value=e.data,console.log("部门人员树:",v.value))}catch(e){console.error("获取部门人员失败:",e)}})(),k()})),(a,t)=>e.e({a:e.unref(y)},e.unref(y)?{b:e.o((a=>e.unref(_)(!0)))}:{},{c:e.t(s.value||"请选择整改责任人"),d:s.value?"":1,e:e.o(S),f:e.o(h),g:c.value},c.value?{h:e.t(m.value)}:{},{i:e.f(v.value,((a,t,u)=>e.e({a:e.t(a.deptName),b:f(a)},(f(a),{}),{c:a.deptId,d:e.n({active:d.value===t}),e:e.o((e=>d.value=t),a.deptId)}))),j:0===p.value.length},0===p.value.length?{}:{k:e.f(p.value,((a,t,u)=>e.e({a:e.t(g(a)),b:String(c.value)===String(a.userId)},(String(c.value),String(a.userId),{}),{c:"user-"+a.userId,d:String(c.value)===String(a.userId)?1:"",e:e.o((e=>{return t=a.userId,void(c.value=String(t));var t}),"user-"+a.userId)})))},{l:"dept-users-"+d.value,m:e.o(h),n:e.o(w),o:e.o(h),p:e.p({show:l.value,mode:"bottom",round:"20"}),q:e.t(x.value||"请选择整改期限"),r:e.n(x.value?"":"text-gray"),s:e.o((e=>I.value=!0)),t:e.o(b),v:e.o((e=>I.value=!1)),w:e.o((e=>I.value=!1)),x:e.o((e=>D.value=e)),y:e.p({show:I.value,mode:"datetime",modelValue:D.value}),z:e.o(T),A:e.o($),B:e.gei(a,"")})}},r=e._export_sfc(n,[["__scopeId","data-v-3aed9080"]]);wx.createPage(r);

View File

@@ -1 +1 @@
<view class="{{['padding', 'page', 'data-v-860f0a5c', virtualHostClass]}}" style="{{virtualHostStyle}}" hidden="{{virtualHostHidden || false}}" id="{{B}}"><view class="padding radius bg-white data-v-860f0a5c"><view wx:if="{{a}}" class="bg-orange-light text-orange padding-sm radius margin-bottom flex justify-between align-center data-v-860f0a5c" style="font-size:24rpx;background-color:#FFF7EB;border:1rpx solid #FFE4CC;width:100%;box-sizing:border-box;display:flex;flex-direction:row;justify-content:space-between;align-items:center;margin-bottom:20rpx"><view class="flex align-center data-v-860f0a5c" style="display:flex;flex-direction:row;align-items:center"><text class="cuIcon-info margin-right-xs data-v-860f0a5c" style="margin-right:10rpx"></text><text class="data-v-860f0a5c">已自动恢复您上次未提交的内容</text></view><text class="text-blue text-bold data-v-860f0a5c" style="cursor:pointer;padding:0 10rpx;color:#2667E9;font-weight:bold" bindtap="{{b}}">清空草稿</text></view><view class="flex margin-bottom data-v-860f0a5c"><view class="text-gray data-v-860f0a5c">整改责任人</view><view class="text-red data-v-860f0a5c">*</view></view><view class="select-trigger data-v-860f0a5c" bindtap="{{e}}"><view class="{{['select-content', 'data-v-860f0a5c', d && 'text-gray']}}">{{c}}</view><text class="cuIcon-unfold data-v-860f0a5c"></text></view><u-popup wx:if="{{p}}" class="data-v-860f0a5c" virtualHostClass="data-v-860f0a5c" u-s="{{['d']}}" bindclose="{{o}}" u-i="860f0a5c-0" bind:__l="__l" u-p="{{p}}"><view class="user-popup cascader-user-popup data-v-860f0a5c"><view class="popup-header data-v-860f0a5c"><view class="popup-title text-bold data-v-860f0a5c">选择整改责任人</view><view class="popup-close data-v-860f0a5c" bindtap="{{f}}">×</view></view><view wx:if="{{g}}" class="selected-summary data-v-860f0a5c"><text class="summary-label data-v-860f0a5c">已选:</text><text class="summary-text data-v-860f0a5c">{{h}}</text></view><view class="cascader-body data-v-860f0a5c"><scroll-view class="cascader-col dept-col data-v-860f0a5c" scroll-y><view wx:for="{{i}}" wx:for-item="dept" wx:key="c" class="{{['data-v-860f0a5c', 'cascader-item', dept.d]}}" bindtap="{{dept.e}}"><text class="cascader-item-text data-v-860f0a5c">{{dept.a}}</text><text wx:if="{{dept.b}}" class="dept-dot data-v-860f0a5c"></text></view></scroll-view><scroll-view class="cascader-col user-col data-v-860f0a5c" scroll-y key="{{l}}"><view wx:if="{{j}}" class="empty-tip data-v-860f0a5c">该部门暂无人员</view><view wx:else class="data-v-860f0a5c"><view wx:for="{{k}}" wx:for-item="user" wx:key="c" class="{{['user-item', 'data-v-860f0a5c', user.d && 'active']}}" bindtap="{{user.e}}"><text class="user-item-text data-v-860f0a5c">{{user.a}}</text><text wx:if="{{user.b}}" class="cuIcon-check text-blue data-v-860f0a5c"></text></view></view></scroll-view></view><view class="popup-footer data-v-860f0a5c"><button class="btn-cancel data-v-860f0a5c" bindtap="{{m}}">取消</button><button class="btn-confirm bg-blue data-v-860f0a5c" bindtap="{{n}}">确定</button></view></view></u-popup><view class="flex margin-bottom margin-top data-v-860f0a5c"><view class="text-gray data-v-860f0a5c">整改期限</view><view class="text-red data-v-860f0a5c">*</view></view><view class="picker-input data-v-860f0a5c" bindtap="{{s}}"><text class="{{['data-v-860f0a5c', r]}}">{{q}}</text></view><up-datetime-picker wx:if="{{y}}" class="data-v-860f0a5c" virtualHostClass="data-v-860f0a5c" bindconfirm="{{t}}" bindcancel="{{v}}" bindclose="{{w}}" u-i="860f0a5c-1" bind:__l="__l" bindupdateModelValue="{{x}}" u-p="{{y}}"></up-datetime-picker><view class="btn-group margin-top-xl data-v-860f0a5c"><button class="btn-cancel data-v-860f0a5c" bindtap="{{z}}">取消</button><button class="btn-confirm bg-blue data-v-860f0a5c" bindtap="{{A}}">确认</button></view></view></view> <view class="{{['padding', 'page', 'data-v-3aed9080', virtualHostClass]}}" style="{{virtualHostStyle}}" hidden="{{virtualHostHidden || false}}" id="{{B}}"><view class="padding radius bg-white data-v-3aed9080"><view wx:if="{{a}}" class="bg-orange-light text-orange padding-sm radius margin-bottom flex justify-between align-center data-v-3aed9080" style="font-size:24rpx;background-color:#FFF7EB;border:1rpx solid #FFE4CC;width:100%;box-sizing:border-box;display:flex;flex-direction:row;justify-content:space-between;align-items:center;margin-bottom:20rpx"><view class="flex align-center data-v-3aed9080" style="display:flex;flex-direction:row;align-items:center"><text class="cuIcon-info margin-right-xs data-v-3aed9080" style="margin-right:10rpx"></text><text class="data-v-3aed9080">已自动恢复您上次未提交的内容</text></view><text class="text-blue text-bold data-v-3aed9080" style="cursor:pointer;padding:0 10rpx;color:#2667E9;font-weight:bold" bindtap="{{b}}">清空草稿</text></view><view class="flex margin-bottom data-v-3aed9080"><view class="text-gray data-v-3aed9080">整改责任人</view><view class="text-red data-v-3aed9080">*</view></view><view class="select-trigger data-v-3aed9080" bindtap="{{e}}"><view class="{{['select-content', 'data-v-3aed9080', d && 'text-gray']}}">{{c}}</view><text class="cuIcon-unfold data-v-3aed9080"></text></view><u-popup wx:if="{{p}}" class="data-v-3aed9080" virtualHostClass="data-v-3aed9080" u-s="{{['d']}}" bindclose="{{o}}" u-i="3aed9080-0" bind:__l="__l" u-p="{{p}}"><view class="user-popup cascader-user-popup data-v-3aed9080"><view class="popup-header data-v-3aed9080"><view class="popup-title text-bold data-v-3aed9080">选择整改责任人</view><view class="popup-close data-v-3aed9080" bindtap="{{f}}">×</view></view><view wx:if="{{g}}" class="selected-summary data-v-3aed9080"><text class="summary-label data-v-3aed9080">已选:</text><text class="summary-text data-v-3aed9080">{{h}}</text></view><view class="cascader-body data-v-3aed9080"><scroll-view class="cascader-col dept-col data-v-3aed9080" scroll-y><view wx:for="{{i}}" wx:for-item="dept" wx:key="c" class="{{['data-v-3aed9080', 'cascader-item', dept.d]}}" bindtap="{{dept.e}}"><text class="cascader-item-text data-v-3aed9080">{{dept.a}}</text><text wx:if="{{dept.b}}" class="dept-dot data-v-3aed9080"></text></view></scroll-view><scroll-view class="cascader-col user-col data-v-3aed9080" scroll-y key="{{l}}"><view wx:if="{{j}}" class="empty-tip data-v-3aed9080">该部门暂无人员</view><view wx:else class="data-v-3aed9080"><view wx:for="{{k}}" wx:for-item="user" wx:key="c" class="{{['user-item', 'data-v-3aed9080', user.d && 'active']}}" bindtap="{{user.e}}"><text class="user-item-text data-v-3aed9080">{{user.a}}</text><text wx:if="{{user.b}}" class="cuIcon-check text-blue data-v-3aed9080"></text></view></view></scroll-view></view><view class="popup-footer data-v-3aed9080"><button class="btn-cancel data-v-3aed9080" bindtap="{{m}}">取消</button><button class="btn-confirm bg-blue data-v-3aed9080" bindtap="{{n}}">确定</button></view></view></u-popup><view class="flex margin-bottom margin-top data-v-3aed9080"><view class="text-gray data-v-3aed9080">整改期限</view><view class="text-red data-v-3aed9080">*</view></view><view class="picker-input data-v-3aed9080" bindtap="{{s}}"><text class="{{['data-v-3aed9080', r]}}">{{q}}</text></view><up-datetime-picker wx:if="{{y}}" class="data-v-3aed9080" virtualHostClass="data-v-3aed9080" bindconfirm="{{t}}" bindcancel="{{v}}" bindclose="{{w}}" u-i="3aed9080-1" bind:__l="__l" bindupdateModelValue="{{x}}" u-p="{{y}}"></up-datetime-picker><view class="btn-group margin-top-xl data-v-3aed9080"><button class="btn-cancel data-v-3aed9080" bindtap="{{z}}">取消</button><button class="btn-confirm bg-blue data-v-3aed9080" bindtap="{{A}}">确认</button></view></view></view>

View File

@@ -1 +1 @@
.page.data-v-860f0a5c{min-height:100vh;background:#ebf2fc}.picker-input.data-v-860f0a5c{background:#fff;border-radius:8rpx;padding:24rpx 20rpx;margin-bottom:20rpx;border:1rpx solid #F6F6F6}.picker-input text.data-v-860f0a5c{font-size:28rpx;color:#333}.select-trigger.data-v-860f0a5c{display:flex;align-items:center;justify-content:space-between;background:#fff;border:1rpx solid #dcdfe6;border-radius:8rpx;padding:20rpx 24rpx;margin-bottom:20rpx}.select-trigger .select-content.data-v-860f0a5c{flex:1;font-size:28rpx;color:#333}.user-popup.data-v-860f0a5c{background:#fff}.user-popup .popup-header.data-v-860f0a5c{display:flex;justify-content:space-between;align-items:center;padding:30rpx;border-bottom:1rpx solid #eee}.user-popup .popup-header .popup-title.data-v-860f0a5c{font-size:32rpx;color:#333}.user-popup .popup-header .popup-close.data-v-860f0a5c{font-size:40rpx;color:#999;line-height:1}.user-popup.cascader-user-popup .selected-summary.data-v-860f0a5c{padding:16rpx 30rpx;background:#f5f7fa;border-bottom:1rpx solid #eee;font-size:24rpx;line-height:1.5}.user-popup.cascader-user-popup .selected-summary .summary-label.data-v-860f0a5c{color:#909399}.user-popup.cascader-user-popup .selected-summary .summary-text.data-v-860f0a5c{color:#333}.user-popup.cascader-user-popup .cascader-body.data-v-860f0a5c{display:flex;height:600rpx}.user-popup.cascader-user-popup .cascader-col.data-v-860f0a5c{height:600rpx;box-sizing:border-box}.user-popup.cascader-user-popup .dept-col.data-v-860f0a5c{width:38%;background:#f7f8fa;border-right:1rpx solid #eee}.user-popup.cascader-user-popup .user-col.data-v-860f0a5c{width:62%;padding:10rpx 20rpx;box-sizing:border-box}.user-popup.cascader-user-popup .cascader-item.data-v-860f0a5c{display:flex;align-items:center;justify-content:space-between;padding:28rpx 24rpx;font-size:28rpx;color:#333;border-bottom:1rpx solid #eef0f3}.user-popup.cascader-user-popup .cascader-item.active.data-v-860f0a5c{background:#fff;color:#2667e9;font-weight:600;position:relative}.user-popup.cascader-user-popup .cascader-item.active.data-v-860f0a5c:before{content:"";position:absolute;left:0;top:0;bottom:0;width:6rpx;background:#2667e9}.user-popup.cascader-user-popup .cascader-item-text.data-v-860f0a5c{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.user-popup.cascader-user-popup .dept-dot.data-v-860f0a5c{width:12rpx;height:12rpx;border-radius:50%;background:#2667e9;margin-left:8rpx;flex-shrink:0}.user-popup.cascader-user-popup .empty-tip.data-v-860f0a5c{padding:80rpx 20rpx;text-align:center;color:#909399;font-size:26rpx}.user-popup .user-item.data-v-860f0a5c{display:flex;align-items:center;justify-content:space-between;padding:24rpx 0;border-bottom:1rpx solid #f5f5f5}.user-popup .user-item.data-v-860f0a5c:last-child{border-bottom:none}.user-popup .user-item.active .user-item-text.data-v-860f0a5c{color:#2667e9;font-weight:600}.user-popup .user-item .user-item-text.data-v-860f0a5c{flex:1;font-size:28rpx;color:#333}.user-popup .popup-footer.data-v-860f0a5c{display:flex;gap:24rpx;padding:24rpx 30rpx;padding-bottom:calc(24rpx + env(safe-area-inset-bottom));background:#fff}.user-popup .popup-footer button.data-v-860f0a5c{flex:1;height:80rpx;line-height:80rpx;border-radius:40rpx;font-size:30rpx;margin:0;padding:0}.user-popup .popup-footer button.data-v-860f0a5c:after{border:none}.user-popup .popup-footer .btn-cancel.data-v-860f0a5c{background:#fff;color:#2667e9;border:2rpx solid #2667E9}.user-popup .popup-footer .btn-confirm.data-v-860f0a5c{color:#fff;border:none}.btn-group.data-v-860f0a5c{display:flex;gap:30rpx}.btn-cancel.data-v-860f0a5c{flex:1;height:80rpx;line-height:80rpx;border:2rpx solid #2667E9;border-radius:40rpx;background:#fff;color:#2667e9;font-size:30rpx}.btn-confirm.data-v-860f0a5c{flex:1;height:80rpx;line-height:80rpx;border-radius:40rpx;color:#fff;font-size:30rpx} .page.data-v-3aed9080{min-height:100vh;background:#ebf2fc}.picker-input.data-v-3aed9080{background:#fff;border-radius:8rpx;padding:24rpx 20rpx;margin-bottom:20rpx;border:1rpx solid #F6F6F6}.picker-input text.data-v-3aed9080{font-size:28rpx;color:#333}.select-trigger.data-v-3aed9080{display:flex;align-items:center;justify-content:space-between;background:#fff;border:1rpx solid #dcdfe6;border-radius:8rpx;padding:20rpx 24rpx;margin-bottom:20rpx}.select-trigger .select-content.data-v-3aed9080{flex:1;font-size:28rpx;color:#333}.user-popup.data-v-3aed9080{background:#fff}.user-popup .popup-header.data-v-3aed9080{display:flex;justify-content:space-between;align-items:center;padding:30rpx;border-bottom:1rpx solid #eee}.user-popup .popup-header .popup-title.data-v-3aed9080{font-size:32rpx;color:#333}.user-popup .popup-header .popup-close.data-v-3aed9080{font-size:40rpx;color:#999;line-height:1}.user-popup.cascader-user-popup .selected-summary.data-v-3aed9080{padding:16rpx 30rpx;background:#f5f7fa;border-bottom:1rpx solid #eee;font-size:24rpx;line-height:1.5}.user-popup.cascader-user-popup .selected-summary .summary-label.data-v-3aed9080{color:#909399}.user-popup.cascader-user-popup .selected-summary .summary-text.data-v-3aed9080{color:#333}.user-popup.cascader-user-popup .cascader-body.data-v-3aed9080{display:flex;height:600rpx}.user-popup.cascader-user-popup .cascader-col.data-v-3aed9080{height:600rpx;box-sizing:border-box}.user-popup.cascader-user-popup .dept-col.data-v-3aed9080{width:38%;background:#f7f8fa;border-right:1rpx solid #eee}.user-popup.cascader-user-popup .user-col.data-v-3aed9080{width:62%;padding:10rpx 20rpx;box-sizing:border-box}.user-popup.cascader-user-popup .cascader-item.data-v-3aed9080{display:flex;align-items:center;justify-content:space-between;padding:28rpx 24rpx;font-size:28rpx;color:#333;border-bottom:1rpx solid #eef0f3}.user-popup.cascader-user-popup .cascader-item.active.data-v-3aed9080{background:#fff;color:#2667e9;font-weight:600;position:relative}.user-popup.cascader-user-popup .cascader-item.active.data-v-3aed9080:before{content:"";position:absolute;left:0;top:0;bottom:0;width:6rpx;background:#2667e9}.user-popup.cascader-user-popup .cascader-item-text.data-v-3aed9080{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.user-popup.cascader-user-popup .dept-dot.data-v-3aed9080{width:12rpx;height:12rpx;border-radius:50%;background:#2667e9;margin-left:8rpx;flex-shrink:0}.user-popup.cascader-user-popup .empty-tip.data-v-3aed9080{padding:80rpx 20rpx;text-align:center;color:#909399;font-size:26rpx}.user-popup .user-item.data-v-3aed9080{display:flex;align-items:center;justify-content:space-between;padding:24rpx 0;border-bottom:1rpx solid #f5f5f5}.user-popup .user-item.data-v-3aed9080:last-child{border-bottom:none}.user-popup .user-item.active .user-item-text.data-v-3aed9080{color:#2667e9;font-weight:600}.user-popup .user-item .user-item-text.data-v-3aed9080{flex:1;font-size:28rpx;color:#333}.user-popup .popup-footer.data-v-3aed9080{display:flex;gap:24rpx;padding:24rpx 30rpx;padding-bottom:calc(24rpx + env(safe-area-inset-bottom));background:#fff}.user-popup .popup-footer button.data-v-3aed9080{flex:1;height:80rpx;line-height:80rpx;border-radius:40rpx;font-size:30rpx;margin:0;padding:0}.user-popup .popup-footer button.data-v-3aed9080:after{border:none}.user-popup .popup-footer .btn-cancel.data-v-3aed9080{background:#fff;color:#2667e9;border:2rpx solid #2667E9}.user-popup .popup-footer .btn-confirm.data-v-3aed9080{color:#fff;border:none}.btn-group.data-v-3aed9080{display:flex;gap:30rpx}.btn-cancel.data-v-3aed9080{flex:1;height:80rpx;line-height:80rpx;border:2rpx solid #2667E9;border-radius:40rpx;background:#fff;color:#2667e9;font-size:30rpx}.btn-confirm.data-v-3aed9080{flex:1;height:80rpx;line-height:80rpx;border-radius:40rpx;color:#fff;font-size:30rpx}

View File

@@ -0,0 +1 @@
"use strict";const e=require("../../common/vendor.js"),t=require("../../common/assets.js"),a=require("../../request/api.js");if(!Array){e.resolveComponent("u-navbar")()}Math||((()=>"../../uni_modules/uview-plus/components/u-navbar/u-navbar.js")+o)();const o=()=>"../../components/hazardDetail/HazardDetailPanelV2.js",n={__name:"detail2",setup(o){const n=e.getCurrentInstance(),i=(null==n?void 0:n.proxy)||n,r=e.ref({}),s=e.ref(!1),l=e.ref(0),u=()=>{e.nextTick$1((()=>{const t=e.index.createSelectorQuery().in(i);t.select(".panel-wrap").boundingClientRect(),t.exec((t=>{const a=null==t?void 0:t[0];if((null==a?void 0:a.height)>0)return void(l.value=Math.floor(a.height));const o=e.index.getSystemInfoSync();l.value=Math.floor(.55*o.windowHeight)}))}))};return e.onReady((()=>{u(),setTimeout(u,100),setTimeout(u,400)})),e.watch(s,(e=>{e||(u(),setTimeout(u,100),setTimeout(u,400))})),e.onLoad((t=>{t.hazardId?(async t=>{s.value=!0;try{const o=await a.getHazardDetail(t);0===o.code&&o.data?r.value=o.data:e.index.showToast({title:o.msg||"获取详情失败",icon:"none"})}catch(o){console.error("获取隐患详情失败:",o),e.index.showToast({title:"获取详情失败",icon:"none"})}finally{s.value=!1,u(),setTimeout(u,100),setTimeout(u,400)}})(t.hazardId):(e.index.showToast({title:"缺少隐患ID",icon:"none"}),setTimeout((()=>{e.index.navigateBack()}),1500))})),(a,o)=>({a:e.p({title:"查看隐患",placeholder:!0,safeAreaInsetTop:!0,bgColor:"transparent",titleColor:"#ffffff",leftIconColor:"#ffffff",autoBack:!0,border:!1}),b:t._imports_0$1,c:e.t(r.value.statusName||"-"),d:t._imports_1,e:e.t(r.value.createdAt||"-"),f:e.p({detail:r.value,loading:s.value,"body-height":l.value}),g:e.gei(a,"")})}},i=e._export_sfc(n,[["__scopeId","data-v-b921b17f"]]);wx.createPage(i);

View File

@@ -0,0 +1,10 @@
{
"navigationBarTitleText": "隐患详情",
"navigationStyle": "custom",
"navigationBarTextStyle": "white",
"disableScroll": true,
"usingComponents": {
"u-navbar": "../../uni_modules/uview-plus/components/u-navbar/u-navbar",
"hazard-detail-panel-v2": "../../components/hazardDetail/HazardDetailPanelV2"
}
}

View File

@@ -0,0 +1 @@
<view class="{{['page', 'data-v-b921b17f', virtualHostClass]}}" style="{{virtualHostStyle}}" hidden="{{virtualHostHidden || false}}" id="{{g}}"><view class="top-gradient-wrap data-v-b921b17f"><u-navbar wx:if="{{a}}" class="data-v-b921b17f" virtualHostClass="data-v-b921b17f" u-i="b921b17f-0" bind:__l="__l" u-p="{{a}}"/><view class="summary-card data-v-b921b17f"><view class="summary-side summary-side--left data-v-b921b17f"><image class="summary-icon data-v-b921b17f" src="{{b}}" mode="aspectFit"/><view class="summary-icon-gap data-v-b921b17f"><view class="summary-text data-v-b921b17f"><view class="summary-label data-v-b921b17f">隐患状态</view><view class="summary-status data-v-b921b17f">{{c}}</view></view></view></view><view class="summary-divider data-v-b921b17f"></view><view class="summary-side summary-side--right data-v-b921b17f"><image class="summary-icon data-v-b921b17f" src="{{d}}" mode="aspectFit"/><view class="summary-text data-v-b921b17f"><view class="summary-label data-v-b921b17f">提交日期</view><view class="summary-date data-v-b921b17f">{{e}}</view></view></view></view></view><view class="panel-wrap data-v-b921b17f"><hazard-detail-panel-v2 wx:if="{{f}}" class="data-v-b921b17f" virtualHostClass="data-v-b921b17f" u-i="b921b17f-1" bind:__l="__l" u-p="{{f}}"/></view></view>

View File

@@ -0,0 +1 @@
.page.data-v-b921b17f{height:100vh;overflow:hidden;display:flex;flex-direction:column;box-sizing:border-box;background:#f5f7fa}.top-gradient-wrap.data-v-b921b17f{flex-shrink:0;background:linear-gradient(180deg,#046cea,#2158c8 28.44%,rgba(4,107,234,0))}.summary-card.data-v-b921b17f{margin:32rpx 30rpx 0;padding:28rpx 30rpx 32rpx;background:#fff;border-radius:20rpx;display:flex;align-items:flex-start;box-sizing:border-box}.summary-side.data-v-b921b17f{display:flex;align-items:flex-start}.summary-side--left.data-v-b921b17f{flex-shrink:0;align-items:center}.summary-side--left .summary-text.data-v-b921b17f{padding-top:0;padding-bottom:0}.summary-side--right.data-v-b921b17f{flex:1;min-width:0;align-items:center}.summary-side--right .summary-icon.data-v-b921b17f{margin-right:22rpx}.summary-side--right .summary-text.data-v-b921b17f{flex:1;min-width:0;padding-top:0;padding-bottom:0}.summary-icon-gap.data-v-b921b17f{width:149rpx;flex-shrink:0;box-sizing:border-box}.summary-icon.data-v-b921b17f{width:55rpx;height:65rpx;flex-shrink:0}.summary-side--left .summary-icon.data-v-b921b17f{margin-right:22rpx}.summary-text.data-v-b921b17f{flex-shrink:0;box-sizing:border-box}.summary-label.data-v-b921b17f{font-size:24rpx;color:#8f9ca2;line-height:34rpx}.summary-status.data-v-b921b17f{margin-top:8rpx;font-size:28rpx;font-weight:400;color:#333;line-height:40rpx}.summary-date.data-v-b921b17f{margin-top:8rpx;font-size:28rpx;font-weight:400;color:#333;line-height:40rpx;white-space:nowrap}.summary-divider.data-v-b921b17f{width:2rpx;height:72rpx;flex-shrink:0;background:#eee;margin-right:40rpx}.panel-wrap.data-v-b921b17f{flex:1;min-height:0;height:0;margin-top:12rpx;padding:0 30rpx 30rpx;box-sizing:border-box;display:flex;flex-direction:column;overflow:hidden}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
"use strict";const e=require("../../common/vendor.js"),a=require("../../request/api.js"),t=require("../../request/request.js");if(!Array){(e.resolveComponent("up-input")+e.resolveComponent("up-textarea"))()}Math||((()=>"../../uni_modules/uview-plus/components/u-input/u-input.js")+(()=>"../../uni_modules/uview-plus/components/u-textarea/u-textarea.js"))();const r={__name:"view",setup(r){const o=e.reactive({hazardId:"",assignId:"",title:"",level:0,levelName:"",source:"",description:"",address:"",areaName:"",areaColor:"",tagName:"",legalBasis:"",regulationName:"",attachments:[]}),s=e.computed((()=>o.legalBasis||o.regulationName||"")),l=e=>t.toImageUrl(e);return e.onLoad((t=>{t.hazardId&&(async(t,r)=>{try{const s={hazardId:t};r&&(s.assignId=r);const l=await a.getHiddenDangerDetail(s);0===l.code&&l.data?Object.assign(o,l.data):e.index.showToast({title:l.msg||"获取详情失败",icon:"none"})}catch(s){console.error("获取隐患详情失败:",s),e.index.showToast({title:"请求失败",icon:"none"})}})(t.hazardId,t.assignId)})),(a,t)=>e.e({a:e.t(o.source||"暂无"),b:o.attachments&&o.attachments.length>0},o.attachments&&o.attachments.length>0?{c:e.f(o.attachments,((a,t,r)=>({a:t,b:l(a.filePath),c:e.o((a=>{return r=t,void(o.attachments&&0!==o.attachments.length&&e.index.previewImage({current:r,urls:o.attachments.map((e=>l(e.filePath)))}));var r}),t)})))}:{},{d:e.o((e=>o.title=e)),e:e.p({disabled:!0,disabledColor:"#F6F6F6",border:"surround",placeholder:"暂无",modelValue:o.title}),f:e.n(2===o.level?"bg-blue light":"bg-gray"),g:e.n(3===o.level?"bg-blue light":"bg-gray"),h:e.o((e=>o.address=e)),i:e.p({disabled:!0,disabledColor:"#F6F6F6",border:"surround",placeholder:"暂无地址",modelValue:o.address}),j:e.t(s.value||"暂无"),k:s.value?"":1,l:o.areaColor},o.areaColor?{m:o.areaColor}:{},{n:e.t(o.areaName||"暂无"),o:o.areaName?"":1,p:e.o((e=>o.description=e)),q:e.p({placeholder:"暂无描述",disabled:!0,autoHeight:!0,modelValue:o.description}),r:e.t(o.tagName||"暂无"),s:e.gei(a,"")})}},o=e._export_sfc(r,[["__scopeId","data-v-52ff0d96"]]);wx.createPage(o); "use strict";const e=require("../../common/vendor.js"),a=require("../../request/api.js");Math||t();const t=()=>"../../components/hazardDetail/HazardDetailPanel.js",o={__name:"view",setup(t){const o=e.ref({}),n=e.ref(!1);return e.onLoad((t=>{t.hazardId?(async t=>{n.value=!0;try{const i=await a.getHazardDetail(t);0===i.code&&i.data?o.value=i.data:e.index.showToast({title:i.msg||"获取详情失败",icon:"none"})}catch(i){console.error("获取隐患详情失败:",i)}finally{n.value=!1}})(t.hazardId):(e.index.showToast({title:"缺少隐患ID",icon:"none"}),setTimeout((()=>{e.index.navigateBack()}),1500))})),(a,t)=>({a:e.p({detail:o.value,loading:n.value}),b:e.gei(a,"")})}},n=e._export_sfc(o,[["__scopeId","data-v-c54bf299"]]);wx.createPage(n);

View File

@@ -1,7 +1,6 @@
{ {
"navigationBarTitleText": "查看隐患", "navigationBarTitleText": "查看隐患",
"usingComponents": { "usingComponents": {
"up-input": "../../uni_modules/uview-plus/components/u-input/u-input", "hazard-detail-panel": "../../components/hazardDetail/HazardDetailPanel"
"up-textarea": "../../uni_modules/uview-plus/components/u-textarea/u-textarea"
} }
} }

View File

@@ -1 +1 @@
<view class="{{['padding', 'page', 'data-v-52ff0d96', virtualHostClass]}}" style="{{virtualHostStyle}}" hidden="{{virtualHostHidden || false}}" id="{{s}}"><view class="padding bg-white radius data-v-52ff0d96"><view class="flex margin-bottom data-v-52ff0d96"><view class="text-gray data-v-52ff0d96">检查形式</view><view class="text-red data-v-52ff0d96">*</view></view><view class="read-only-box data-v-52ff0d96">{{a}}</view><view class="flex margin-bottom margin-top data-v-52ff0d96"><view class="text-gray data-v-52ff0d96">隐患图片</view><view class="text-red data-v-52ff0d96">*</view></view><view class="margin-bottom data-v-52ff0d96"><view wx:if="{{b}}" class="margin-top-xs data-v-52ff0d96"><view class="flex data-v-52ff0d96" style="flex-wrap:wrap;gap:10rpx"><image wx:for="{{c}}" wx:for-item="img" wx:key="a" class="data-v-52ff0d96" src="{{img.b}}" style="width:136rpx;height:136rpx;border-radius:16rpx" mode="aspectFill" bindtap="{{img.c}}"></image></view></view><view wx:else class="text-gray text-sm data-v-52ff0d96">暂无图片</view><view class="text-gray text-sm margin-top-xs data-v-52ff0d96">必填:请上传现场照片作为隐患证据</view></view><view class="flex margin-bottom margin-top data-v-52ff0d96"><view class="text-gray data-v-52ff0d96">隐患标题</view><view class="text-red data-v-52ff0d96">*</view></view><up-input wx:if="{{e}}" class="data-v-52ff0d96" virtualHostClass="data-v-52ff0d96" u-i="52ff0d96-0" bind:__l="__l" bindupdateModelValue="{{d}}" u-p="{{e}}"/><view class="text-sm text-gray margin-top-xs data-v-52ff0d96">请用简洁的语言概括隐患要点</view><view class="flex margin-bottom margin-top data-v-52ff0d96"><view class="text-gray data-v-52ff0d96">隐患等级</view><view class="text-red data-v-52ff0d96">*</view></view><view class="flex col-2 data-v-52ff0d96" style="gap:10rpx"><view class="{{[f, 'level-item', 'data-v-52ff0d96']}}">一般隐患</view><view class="{{[g, 'level-item', 'data-v-52ff0d96']}}">重大隐患</view></view><view class="flex margin-bottom margin-top data-v-52ff0d96"><view class="text-gray data-v-52ff0d96">隐患位置</view><view class="text-red data-v-52ff0d96">*</view></view><up-input wx:if="{{i}}" class="data-v-52ff0d96" virtualHostClass="data-v-52ff0d96" u-i="52ff0d96-1" bind:__l="__l" bindupdateModelValue="{{h}}" u-p="{{i}}"/><view class="text-gray text-sm margin-top-xs data-v-52ff0d96">如办公楼3层东侧消防通道、生产车间A区设备旁等或点击"选择地址"按钮在地图上选择</view><view class="flex margin-bottom margin-top data-v-52ff0d96"><view class="text-gray data-v-52ff0d96">法律依据</view></view><view class="read-only-select data-v-52ff0d96"><view class="{{['select-value', 'data-v-52ff0d96', k && 'placeholder']}}">{{j}}</view></view><view class="flex margin-bottom margin-top data-v-52ff0d96"><view class="text-gray data-v-52ff0d96">隐患区域</view></view><view class="read-only-select data-v-52ff0d96"><view class="flex align-center data-v-52ff0d96"><view wx:if="{{l}}" class="area-color-dot data-v-52ff0d96" style="{{'background-color:' + m}}"></view><view class="{{['select-value', 'data-v-52ff0d96', o && 'placeholder']}}">{{n}}</view></view></view><view class="flex margin-bottom margin-top data-v-52ff0d96"><view class="text-gray data-v-52ff0d96">隐患描述</view><view class="text-red data-v-52ff0d96">*</view></view><up-textarea wx:if="{{q}}" class="data-v-52ff0d96" virtualHostClass="data-v-52ff0d96" u-i="52ff0d96-2" bind:__l="__l" bindupdateModelValue="{{p}}" u-p="{{q}}"></up-textarea><view class="text-gray text-sm margin-top-xs data-v-52ff0d96">请详细说明隐患现状、潜在风险及影响范围</view><view class="text-gray margin-bottom margin-top data-v-52ff0d96">隐患标签</view><view class="read-only-box data-v-52ff0d96">{{r}}</view></view></view> <view class="{{['page', 'data-v-c54bf299', virtualHostClass]}}" style="{{virtualHostStyle}}" hidden="{{virtualHostHidden || false}}" id="{{b}}"><hazard-detail-panel wx:if="{{a}}" class="data-v-c54bf299" virtualHostClass="data-v-c54bf299" u-i="c54bf299-0" bind:__l="__l" u-p="{{a}}"/></view>

View File

@@ -1 +1 @@
.page.data-v-52ff0d96{min-height:100vh;background:#ebf2fc}.read-only-box.data-v-52ff0d96{background:#f5f5f5;border-radius:8rpx;padding:20rpx 24rpx;font-size:28rpx;color:#333}.read-only-select.data-v-52ff0d96{background:#f5f5f5;border:1rpx solid #dcdfe6;border-radius:8rpx;padding:20rpx 24rpx}.read-only-select .select-value.data-v-52ff0d96{font-size:28rpx;color:#333;line-height:1.5;word-break:break-all}.read-only-select .select-value.placeholder.data-v-52ff0d96{color:#999}.level-item.data-v-52ff0d96{padding:16rpx 40rpx;border-radius:8rpx;text-align:center;font-size:28rpx}.area-color-dot.data-v-52ff0d96{width:24rpx;height:24rpx;border-radius:50%;margin-right:16rpx;flex-shrink:0} .page.data-v-c54bf299{min-height:100vh;background:#ebf2fc}

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,9 @@
{ {
"navigationBarTitleText": "三查一曝光", "navigationBarTitleText": "湘西州“三个一”安全管理平台",
"navigationStyle": "custom", "navigationStyle": "custom",
"navigationBarTextStyle": "white", "navigationBarTextStyle": "white",
"usingComponents": { "usingComponents": {
"u-navbar": "../../uni_modules/uview-plus/components/u-navbar/u-navbar" "u-navbar": "../../uni_modules/uview-plus/components/u-navbar/u-navbar",
"u-icon": "../../uni_modules/uview-plus/components/u-icon/u-icon"
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
"use strict";const e=require("../../common/vendor.js"),t=require("../../common/assets.js");if(!Array){e.resolveComponent("cu-custom")()}const a={__name:"forget",setup(a){const o=e.ref(""),n=e.ref(""),l=e.ref(""),s=e.ref(0),u=e.ref(!1);let i=null;const d=e.computed((()=>{if(0===s.value)return"获取验证码";return`重新获取${s.value<10?`0${s.value}`:s.value}`})),v=()=>{u.value=!u.value},c=()=>{11===o.value.length?s.value>0||(s.value=60,r(),e.index.request({url:"http://example.com/api/code",data:{phone:o.value,type:"forget"},method:"POST",dataType:"json",success:t=>{200!=t.data.code?(e.index.showToast({title:t.data.msg||"获取验证码失败",icon:"none"}),s.value=0,m()):e.index.showToast({title:t.data.msg||"验证码已发送"})},fail:()=>{e.index.showToast({title:"网络请求失败",icon:"none"}),s.value=0,m()}})):e.index.showToast({icon:"none",title:"手机号不正确"})},r=()=>{m(),i=setInterval((()=>{s.value--,0===s.value&&m()}),1e3)},m=()=>{i&&(clearInterval(i),i=null)},h=()=>{11===o.value.length?n.value.length<6?e.index.showToast({icon:"none",title:"密码不正确"}):4===l.value.length?e.index.request({url:"http://example.com/api/forget",data:{phone:o.value,password:n.value,code:l.value},method:"POST",dataType:"json",success:t=>{200!=t.data.code?e.index.showToast({title:t.data.msg||"修改密码失败",icon:"none"}):(e.index.showToast({title:t.data.msg||"修改密码成功"}),setTimeout((()=>{e.index.navigateBack()}),1500))},fail:()=>{e.index.showToast({title:"网络请求失败",icon:"none"})}}):e.index.showToast({icon:"none",title:"验证码不正确"}):e.index.showToast({icon:"none",title:"手机号不正确"})};return e.onUnmounted((()=>{m()})),(a,l)=>({a:e.p({isBack:!0}),b:t._imports_0$5,c:o.value,d:e.o((e=>o.value=e.detail.value)),e:t._imports_1$3,f:!u.value,g:n.value,h:e.o((e=>n.value=e.detail.value)),i:u.value?1:"",j:e.o(v),k:e.t(d.value),l:s.value>0?1:"",m:e.o(c),n:e.o(h),o:e.gei(a,"")})}},o=e._export_sfc(a,[["__scopeId","data-v-330f8649"]]);wx.createPage(o); "use strict";const e=require("../../common/vendor.js"),t=require("../../common/assets.js");if(!Array){e.resolveComponent("cu-custom")()}const a={__name:"forget",setup(a){const o=e.ref(""),n=e.ref(""),l=e.ref(""),s=e.ref(0),u=e.ref(!1);let i=null;const d=e.computed((()=>{if(0===s.value)return"获取验证码";return`重新获取${s.value<10?`0${s.value}`:s.value}`})),v=()=>{u.value=!u.value},c=()=>{11===o.value.length?s.value>0||(s.value=60,r(),e.index.request({url:"http://example.com/api/code",data:{phone:o.value,type:"forget"},method:"POST",dataType:"json",success:t=>{200!=t.data.code?(e.index.showToast({title:t.data.msg||"获取验证码失败",icon:"none"}),s.value=0,m()):e.index.showToast({title:t.data.msg||"验证码已发送"})},fail:()=>{e.index.showToast({title:"网络请求失败",icon:"none"}),s.value=0,m()}})):e.index.showToast({icon:"none",title:"手机号不正确"})},r=()=>{m(),i=setInterval((()=>{s.value--,0===s.value&&m()}),1e3)},m=()=>{i&&(clearInterval(i),i=null)},h=()=>{11===o.value.length?n.value.length<6?e.index.showToast({icon:"none",title:"密码不正确"}):4===l.value.length?e.index.request({url:"http://example.com/api/forget",data:{phone:o.value,password:n.value,code:l.value},method:"POST",dataType:"json",success:t=>{200!=t.data.code?e.index.showToast({title:t.data.msg||"修改密码失败",icon:"none"}):(e.index.showToast({title:t.data.msg||"修改密码成功"}),setTimeout((()=>{e.index.navigateBack()}),1500))},fail:()=>{e.index.showToast({title:"网络请求失败",icon:"none"})}}):e.index.showToast({icon:"none",title:"验证码不正确"}):e.index.showToast({icon:"none",title:"手机号不正确"})};return e.onUnmounted((()=>{m()})),(a,l)=>({a:e.p({isBack:!0}),b:t._imports_0$6,c:o.value,d:e.o((e=>o.value=e.detail.value)),e:t._imports_1$4,f:!u.value,g:n.value,h:e.o((e=>n.value=e.detail.value)),i:u.value?1:"",j:e.o(v),k:e.t(d.value),l:s.value>0?1:"",m:e.o(c),n:e.o(h),o:e.gei(a,"")})}},o=e._export_sfc(a,[["__scopeId","data-v-330f8649"]]);wx.createPage(o);

View File

@@ -1 +1 @@
"use strict";const e=require("../../common/vendor.js"),o=require("../../common/assets.js"),a=require("../../request/api.js"),t={__name:"login",setup(t){const s=e.ref(""),n=e.ref(""),i=e.ref(!0),l=()=>{i.value=!i.value},d=async()=>{if(console.log("点击登录按钮"),console.log("用户名:",s.value),console.log("密码:",n.value),s.value)if(n.value)try{console.log("开始调用登录接口...");const o=await a.login({username:s.value,password:n.value});if(console.log("登录接口返回:",o),0===o.code){o.data.token&&e.index.setStorageSync("token",o.data.token);const a={userId:o.data.userId,username:o.data.username,nickName:o.data.nickName,deptId:o.data.deptId,deptName:o.data.deptName,role:o.data.role,isDept:o.data.isDept};e.index.setStorageSync("userInfo",JSON.stringify(a)),e.index.showToast({title:"登录成功",icon:"success"}),setTimeout((()=>{e.index.reLaunch({url:"/pages/index/index"})}),1500)}else e.index.showToast({title:o.msg||"登录失败",icon:"none"})}catch(o){console.error("登录失败:",o),e.index.showToast({title:"网络请求失败",icon:"none"})}else e.index.showToast({icon:"none",title:"请输入密码"});else e.index.showToast({icon:"none",title:"请输入用户名"})};return(a,t)=>({a:o._imports_0$4,b:o._imports_0$5,c:s.value,d:e.o((e=>s.value=e.detail.value)),e:o._imports_1$3,f:i.value,g:n.value,h:e.o((e=>n.value=e.detail.value)),i:i.value?"/static/index/cl.png":"/static/index/op.png",j:e.o(l),k:e.o(d),l:e.gei(a,"")})}},s=e._export_sfc(t,[["__scopeId","data-v-d18a2e65"]]);wx.createPage(s); "use strict";const e=require("../../common/vendor.js"),o=require("../../common/assets.js"),a=require("../../request/api.js"),t={__name:"login",setup(t){const s=e.ref(""),n=e.ref(""),i=e.ref(!0),l=()=>{i.value=!i.value},d=async()=>{if(console.log("点击登录按钮"),console.log("用户名:",s.value),console.log("密码:",n.value),s.value)if(n.value)try{console.log("开始调用登录接口...");const o=await a.login({username:s.value,password:n.value});if(console.log("登录接口返回:",o),0===o.code){o.data.token&&e.index.setStorageSync("token",o.data.token);const a={userId:o.data.userId,username:o.data.username,nickName:o.data.nickName,deptId:o.data.deptId,deptName:o.data.deptName,role:o.data.role,isDept:o.data.isDept};e.index.setStorageSync("userInfo",JSON.stringify(a)),e.index.showToast({title:"登录成功",icon:"success"}),setTimeout((()=>{e.index.reLaunch({url:"/pages/index/index"})}),1500)}else e.index.showToast({title:o.msg||"登录失败",icon:"none"})}catch(o){console.error("登录失败:",o),e.index.showToast({title:"网络请求失败",icon:"none"})}else e.index.showToast({icon:"none",title:"请输入密码"});else e.index.showToast({icon:"none",title:"请输入用户名"})};return(a,t)=>({a:o._imports_0$5,b:o._imports_0$6,c:s.value,d:e.o((e=>s.value=e.detail.value)),e:o._imports_1$4,f:i.value,g:n.value,h:e.o((e=>n.value=e.detail.value)),i:i.value?"/static/index/cl.png":"/static/index/op.png",j:e.o(l),k:e.o(d),l:e.gei(a,"")})}},s=e._export_sfc(t,[["__scopeId","data-v-6710cd5b"]]);wx.createPage(s);

View File

@@ -1 +1 @@
<view class="{{['content', 'data-v-d18a2e65', virtualHostClass]}}" style="{{virtualHostStyle}}" hidden="{{virtualHostHidden || false}}" id="{{l}}"><view class="header data-v-d18a2e65"><image src="{{a}}" class="bg-image data-v-d18a2e65"></image><view class="padding login data-v-d18a2e65"><view class="text-xl text-black text-bold data-v-d18a2e65">账号登录</view><view class="padding-top data-v-d18a2e65">欢迎登录三查一曝光平台</view></view></view><view class="list data-v-d18a2e65"><view class="list-call data-v-d18a2e65"><image class="img data-v-d18a2e65" src="{{b}}"></image><input class="sl-input data-v-d18a2e65" type="text" placeholder="请输入用户名" value="{{c}}" bindinput="{{d}}"/></view><view class="list-call data-v-d18a2e65"><image class="img data-v-d18a2e65" src="{{e}}"></image><input class="sl-input data-v-d18a2e65" type="text" maxlength="32" placeholder="请输入密码" password="{{f}}" value="{{g}}" bindinput="{{h}}"/><image class="eye-img data-v-d18a2e65" src="{{i}}" bindtap="{{j}}"></image></view></view><view class="padding-lr data-v-d18a2e65"><button class="button-login data-v-d18a2e65" hover-class="button-hover" bindtap="{{k}}"> 登录 </button></view></view> <view class="{{['content', 'data-v-6710cd5b', virtualHostClass]}}" style="{{virtualHostStyle}}" hidden="{{virtualHostHidden || false}}" id="{{l}}"><view class="header data-v-6710cd5b"><image src="{{a}}" class="bg-image data-v-6710cd5b"></image><view class="padding login data-v-6710cd5b"><view class="text-xl text-black text-bold data-v-6710cd5b">账号登录</view><view class="padding-top data-v-6710cd5b">欢迎登录湘西州“三个一”安全管理平台</view></view></view><view class="list data-v-6710cd5b"><view class="list-call data-v-6710cd5b"><image class="img data-v-6710cd5b" src="{{b}}"></image><input class="sl-input data-v-6710cd5b" type="text" placeholder="请输入用户名" value="{{c}}" bindinput="{{d}}"/></view><view class="list-call data-v-6710cd5b"><image class="img data-v-6710cd5b" src="{{e}}"></image><input class="sl-input data-v-6710cd5b" type="text" maxlength="32" placeholder="请输入密码" password="{{f}}" value="{{g}}" bindinput="{{h}}"/><image class="eye-img data-v-6710cd5b" src="{{i}}" bindtap="{{j}}"></image></view></view><view class="padding-lr data-v-6710cd5b"><button class="button-login data-v-6710cd5b" hover-class="button-hover" bindtap="{{k}}"> 登录 </button></view></view>

View File

@@ -1 +1 @@
page.data-v-d18a2e65{background-color:#fff}.content.data-v-d18a2e65{display:flex;flex-direction:column;justify-content:center;background-color:#fff}.header.data-v-d18a2e65{width:100%;position:relative;margin-bottom:0}.header .bg-image.data-v-d18a2e65{width:100%;vertical-align:bottom}.login.data-v-d18a2e65{position:absolute;top:50%;color:#666;font-size:28rpx}.list.data-v-d18a2e65{display:flex;flex-direction:column;padding-top:50rpx;padding-left:70rpx;padding-right:70rpx;background-color:#fff;margin-top:-2rpx}.list .list-call.data-v-d18a2e65{display:flex;flex-direction:row;justify-content:space-between;align-items:center;height:100rpx;color:#333;background:#f5f7fb;border-radius:16rpx;border:2rpx solid #F5F7FB;margin-top:30rpx;padding:0 30rpx}.list .list-call .img.data-v-d18a2e65{width:30rpx;height:36rpx}.list .list-call .sl-input.data-v-d18a2e65{flex:1;text-align:left;font-size:32rpx;margin-left:16rpx}.list .list-call .eye-img.data-v-d18a2e65{width:40rpx;height:40rpx}.agreement.data-v-d18a2e65{display:flex;flex-direction:row;justify-content:space-between;align-items:center;font-size:30rpx;margin-top:30rpx;color:#3d83f6;text-align:center;height:40rpx;line-height:40rpx}.agreement .link.data-v-d18a2e65{font-size:30rpx;color:#3d83f6}.agreement .link.data-v-d18a2e65:active{opacity:.8}.padding-lr.data-v-d18a2e65{padding-left:70rpx;padding-right:70rpx}.button-login.data-v-d18a2e65{color:#fff;font-size:34rpx;width:100%;height:100rpx;background:linear-gradient(90deg,#3e95f1,#4269f5);border-radius:50rpx;line-height:100rpx;text-align:center;margin-left:auto;margin-right:auto;margin-top:130rpx;border:none}.button-login.data-v-d18a2e65:after{border:none}.button-report.data-v-d18a2e65{color:#fff;font-size:34rpx;width:100%;height:100rpx;background:linear-gradient(90deg,#ff7878,#f2505b);border-radius:50rpx;line-height:100rpx;text-align:center;margin-left:auto;margin-right:auto;display:flex;align-items:center;justify-content:center}.margin-top.data-v-d18a2e65{margin-top:30rpx}.button-hover.data-v-d18a2e65{opacity:.8}.text-blue.data-v-d18a2e65{color:#3d83f6;font-size:28rpx}.icon-image.data-v-d18a2e65{width:36rpx;height:36rpx;margin-right:8rpx}.text-xl.data-v-d18a2e65{font-size:36rpx}.text-black.data-v-d18a2e65{color:#000}.text-bold.data-v-d18a2e65{font-weight:700}.padding.data-v-d18a2e65{padding:30rpx}.padding-top.data-v-d18a2e65{padding-top:15rpx}.protocol-box.data-v-d18a2e65{display:flex;justify-content:center;margin-top:40rpx}.protocol-box .protocol-link.data-v-d18a2e65{font-size:28rpx;color:#3d83f6;text-decoration:underline} page.data-v-6710cd5b{background-color:#fff}.content.data-v-6710cd5b{display:flex;flex-direction:column;justify-content:center;background-color:#fff}.header.data-v-6710cd5b{width:100%;position:relative;margin-bottom:0}.header .bg-image.data-v-6710cd5b{width:100%;vertical-align:bottom}.login.data-v-6710cd5b{position:absolute;top:50%;color:#666;font-size:28rpx}.list.data-v-6710cd5b{display:flex;flex-direction:column;padding-top:50rpx;padding-left:70rpx;padding-right:70rpx;background-color:#fff;margin-top:-2rpx}.list .list-call.data-v-6710cd5b{display:flex;flex-direction:row;justify-content:space-between;align-items:center;height:100rpx;color:#333;background:#f5f7fb;border-radius:16rpx;border:2rpx solid #F5F7FB;margin-top:30rpx;padding:0 30rpx}.list .list-call .img.data-v-6710cd5b{width:30rpx;height:36rpx}.list .list-call .sl-input.data-v-6710cd5b{flex:1;text-align:left;font-size:32rpx;margin-left:16rpx}.list .list-call .eye-img.data-v-6710cd5b{width:40rpx;height:40rpx}.agreement.data-v-6710cd5b{display:flex;flex-direction:row;justify-content:space-between;align-items:center;font-size:30rpx;margin-top:30rpx;color:#3d83f6;text-align:center;height:40rpx;line-height:40rpx}.agreement .link.data-v-6710cd5b{font-size:30rpx;color:#3d83f6}.agreement .link.data-v-6710cd5b:active{opacity:.8}.padding-lr.data-v-6710cd5b{padding-left:70rpx;padding-right:70rpx}.button-login.data-v-6710cd5b{color:#fff;font-size:34rpx;width:100%;height:100rpx;background:linear-gradient(90deg,#3e95f1,#4269f5);border-radius:50rpx;line-height:100rpx;text-align:center;margin-left:auto;margin-right:auto;margin-top:130rpx;border:none}.button-login.data-v-6710cd5b:after{border:none}.button-report.data-v-6710cd5b{color:#fff;font-size:34rpx;width:100%;height:100rpx;background:linear-gradient(90deg,#ff7878,#f2505b);border-radius:50rpx;line-height:100rpx;text-align:center;margin-left:auto;margin-right:auto;display:flex;align-items:center;justify-content:center}.margin-top.data-v-6710cd5b{margin-top:30rpx}.button-hover.data-v-6710cd5b{opacity:.8}.text-blue.data-v-6710cd5b{color:#3d83f6;font-size:28rpx}.icon-image.data-v-6710cd5b{width:36rpx;height:36rpx;margin-right:8rpx}.text-xl.data-v-6710cd5b{font-size:36rpx}.text-black.data-v-6710cd5b{color:#000}.text-bold.data-v-6710cd5b{font-weight:700}.padding.data-v-6710cd5b{padding:30rpx}.padding-top.data-v-6710cd5b{padding-top:15rpx}.protocol-box.data-v-6710cd5b{display:flex;justify-content:center;margin-top:40rpx}.protocol-box .protocol-link.data-v-6710cd5b{font-size:28rpx;color:#3d83f6;text-decoration:underline}

Some files were not shown because too many files have changed in this diff Show More