Browse Source

扫码入企登记页面

dev-V1.0.1
wangmingyong 6 months ago
parent
commit
da837b54ed
  1. 431
      zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/register/EnforcementInfoSection.vue
  2. 76
      zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/register/EnterpriseInfoSection.vue
  3. 461
      zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/register/RegisterPopups.vue
  4. 2
      zdxt-web-client/zdxt-efcode-enforcement-app/src/router/enforcement-app/enf-main-router.js
  5. 1418
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enforce/enforcementRegister.vue
  6. 226
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enterpriseScanPreview.vue
  7. 14
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/scanEntQRCode.vue

431
zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/register/EnforcementInfoSection.vue

@ -0,0 +1,431 @@
<template>
<div class="section">
<div class="section-title">执法信息</div>
<!-- 是否关联预告 -->
<van-field v-model="formData.relatedNotice" is-link readonly name="relatedNotice" label="是否关联预告"
placeholder="请选择" @click="$emit('showNoticePicker')" clearable />
<!-- 执法单位 -->
<van-field v-model="formData.enforcementUnit" name="enforcementUnit" label="执法单位" placeholder="请输入执法单位"
:rules="[{ required: true, message: '请填写执法单位' }]" required />
<!-- 执法人员 -->
<div class="form-row required">
<div class="form-label"><span class="required-mark">*</span>执法人员</div>
<div class="form-value person-select">
<div class="person-item default-person">
<span>{{ defaultPerson?.name }} {{ defaultPerson?.id }}</span>
</div>
<div v-for="(person, index) in additionalPersons" :key="index" class="person-item-add">
<span @click="$emit('openPersonPicker', index)" :class="{ placeholder: !person.name }">
{{ person.name || '请选择' }}
</span>
<van-icon name="close" class="remove-icon" @click="$emit('removePerson', index)" />
</div>
<div class="person-add">
<span @click="$emit('openPersonPicker', -1)">请选择</span>
<van-icon name="add-o" class="add-circle-icon" @click="$emit('addPerson')" />
</div>
</div>
</div>
<!-- 隐藏字段用于验证执法人员 -->
<van-field v-model="hasPerson" name="enforcementPerson" :rules="[{ required: true, message: '请选择执法人员' }]"
style="display: none;" />
<!-- 执法类别 -->
<van-field v-model="formData.category" is-link readonly name="category" label="执法类别" placeholder="请选择"
@click="$emit('showCategoryPicker')" clearable :rules="[{ required: true, message: '请选择执法类别' }]" required />
<!-- 任务来源 -->
<van-field v-model="formData.taskSource" is-link readonly name="taskSource" label="任务来源" placeholder="请选择"
@click="$emit('showTaskSourcePicker')" clearable :rules="[{ required: true, message: '请选择任务来源' }]"
required />
<!-- 执法事项 -->
<van-field v-model="formData.enforcementMatter" name="enforcementMatter" label="执法事项" placeholder="请输入执法事项"
type="textarea" :rules="[{ required: true, message: '请填写执法事项' }]" required rows="3" maxlength="1000"
show-word-limit />
<!-- 执法依据 -->
<div class="basis-container">
<div v-for="(basis, index) in formData.enforcementBases" :key="index" class="form-row basis-row">
<div class="form-label">执法依据</div>
<div class="form-value basis-wrapper">
<span @click="$emit('openBasisPicker', index)" :class="{ placeholder: !basis }">
{{ basis || '请选择' }}
</span>
<van-icon v-if="index === formData.enforcementBases.length - 1" name="add-o" class="add-circle-icon"
@click.stop="$emit('addBasis')" />
<van-icon v-else name="close" class="close-btn" @click.stop="$emit('removeBasis', index)" />
</div>
</div>
</div>
<!-- 原因 -->
<van-field v-model="formData.reason" name="reason" label="原因" placeholder="请输入原因" readonly />
<!-- 执法时间 -->
<van-field v-model="formData.enforcementTime" name="enforcementTime" label="执法时间" placeholder="请选择执法时间"
readonly />
<!-- <van-field v-model="formData.enforcementTime" name="enforcementTime" label="执法时间" placeholder="请选择执法时间" is-link
readonly :rules="[{ required: true, message: '请选择执法时间' }]" required /> -->
<!-- 联合执法 -->
</div>
<div class="form-section">
<div class="">
<van-field name="radio" label="联合执法" required>
<template #input>
<van-radio-group v-model="formData.jointEnforcement" direction="horizontal">
<van-radio name="0" shape="square">否</van-radio>
<van-radio name="1" shape="square">是</van-radio>
</van-radio-group>
</template>
</van-field>
<!-- <div class="form-label"><span class="required-mark">*</span>联合执法</div>
<div class="form-value radio-group">
<label :class="['radio-option', { active: formData.jointEnforcement === false }]"
@click="formData.jointEnforcement = false">
否
</label>
<label :class="['radio-option', { active: formData.jointEnforcement === true }]"
@click="formData.jointEnforcement = true">
是
</label>
</div> -->
</div>
<!-- 隐藏字段用于验证联合执法 -->
<van-field v-model="jointEnforcementValue" name="jointEnforcement"
:rules="[{ required: true, message: '请选择联合执法' }]" style="display: none;" />
</div>
<div class="form-entrusted">
<!-- 受委托执法 -->
<div class="entrusted-enforcement">
<van-field name="radio" label="受委托执法" required>
<template #input>
<van-radio-group v-model="formData.entrustedEnforcement" direction="horizontal">
<van-radio name="0" shape="square">否</van-radio>
<van-radio name="1" shape="square">是</van-radio>
</van-radio-group>
</template>
</van-field>
<!-- <div class="form-label"><span class="required-mark">*</span>受委托执法</div>
<div class="form-value radio-group">
<label :class="['radio-option', { active: formData.entrustedEnforcement === false }]"
@click="formData.entrustedEnforcement = false">
否
</label>
<label :class="['radio-option', { active: formData.entrustedEnforcement === true }]"
@click="formData.entrustedEnforcement = true">
是
</label>
</div> -->
</div>
<!-- 隐藏字段用于验证受委托执法 -->
<van-field v-model="entrustedEnforcementValue" name="entrustedEnforcement"
:rules="[{ required: true, message: '请选择受委托执法' }]" style="display: none;" />
<div class="form-hint">本次执法是否为受委托事项</div>
<!-- 附件 -->
<div class="file-upload-row">
<div class="file-upload-label">附件 <span class="form-text">(不多于8张)</span></div>
<div class="uploader-wrapper">
<van-uploader v-model="formData.fileList" multiple :max-count="8" :after-read="onAfterReadImage" />
</div>
</div>
</div>
</template>
<script setup name="EnforcementInfoSection">
import { computed } from 'vue';
import { uploadFile } from '@/util/fileUpload';
const props = defineProps({
formData: {
type: Object,
required: true
},
defaultPerson: {
type: Object,
default: null
},
additionalPersons: {
type: Array,
default: () => []
}
});
defineEmits([
'showNoticePicker',
'openPersonPicker',
'removePerson',
'showCategoryPicker',
'showTaskSourcePicker',
'openBasisPicker',
'addBasis',
'removeBasis'
]);
// 计算属性:是否有执法人员
const hasPerson = computed(() => {
return props.defaultPerson?.name || props.additionalPersons.some(p => p.name);
});
// 联合执法值(用于验证)
const jointEnforcementValue = computed(() => {
return props.formData.jointEnforcement !== undefined ? '已选择' : '';
});
// 受委托执法值(用于验证)
const entrustedEnforcementValue = computed(() => {
return props.formData.entrustedEnforcement !== undefined ? '已选择' : '';
});
// 附件上传处理
const onAfterReadImage = (file) => {
const files = Array.isArray(file) ? file : [file];
files.forEach(f => {
uploadFile(f, (res) => {
const data = res && res.data ? res.data : res;
const filePath = typeof data === 'string' ? data : (data && (data.url || data.fileUrl || data.path)) || '';
if (filePath) {
f.url = filePath;
f.status = 'done';
}
});
});
};
</script>
<style lang="less" scoped>
.section {
background: #fff;
border-radius: 0.16rem;
margin-bottom: 0.24rem;
overflow: hidden;
:deep(.van-field__label) {
margin-right: 6px;
color: #000;
}
}
.section-title {
padding: 0.28rem 0.32rem;
font-size: 16px;
font-weight: 600;
color: #000;
}
.form-row {
display: flex;
justify-content: space-between;
// align-items: center;
padding: 0.32rem;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
&.clickable {
&:active {
background: #f9f9f9;
}
}
&.required .form-label {
position: relative;
padding-left: 0.1rem;
}
&.radio-row {
padding-top: 0.24rem;
padding-bottom: 0.24rem;
}
}
.attachment-row {}
.required-mark {
color: #ee0a24;
font-size: 0.32rem;
margin-right: 0.08rem;
font-weight: bold;
}
.form-label {
font-size: 14px;
color: #333;
flex: none;
width: 2.4rem;
font-weight: 500;
}
.form-value {
flex: 1;
font-size: 14px;
color: #333;
display: flex;
align-items: center;
.placeholder {
color: #c8c9cc;
}
}
.person-select {
// margin-left: 30px;
display: block;
// flex-direction: column;
// align-items: flex-end;
// gap: 0.16rem;
}
.person-item {
display: flex;
align-items: center;
font-size: 14px;
&.default-person span {
color: #333;
font-size: 14px;
}
}
.person-item-add {
padding: 10px 10px 0px 0px;
display: flex;
justify-content: space-between;
align-items: center;
.remove-icon {
font-size: 16px;
}
}
.person-add {
display: flex;
align-items: center;
justify-content: space-between;
align-items: center;
color: #c8c9cc;
font-size: 14px;
padding: 10px 10px 0px 0px;
.add-circle-icon {
font-size: 16px;
color: #333;
}
}
.basis-container {
margin-bottom: 0.12rem;
margin-left: 6px;
}
.basis-row {
padding: 0.24rem 0.32rem;
}
.basis-wrapper {
margin-left: 8px;
display: flex;
align-items: end;
justify-content: space-between;
padding-right: 10px;
.close-btn {
font-size: 16px;
}
.add-circle-icon {
font-size: 16px;
}
}
.form-section {
margin-bottom: 10px;
:deep(.van-field__value) {
display: flex;
justify-content: flex-end;
color: #000;
}
}
.form-entrusted {
height: 200px;
background-color: #fff;
.entrusted-enforcement {
:deep(.van-field__value) {
display: flex;
justify-content: flex-end;
color: #000;
}
}
}
.radio-option {
padding: 0.12rem 0.48rem;
border: 1px solid #dcdee0;
border-radius: 0.06rem;
font-size: 0.3rem;
color: #666;
transition: all 0.2s;
&:active {
transform: scale(0.95);
}
&.active {
border-color: #1367fe;
color: #1367fe;
background: rgba(19, 103, 254, 0.05);
}
}
.form-hint {
font-size: 12px;
color: #999;
padding: 0.12rem 0.32rem 0.24rem;
text-align: right;
}
.attachment-wrapper {
flex-direction: column;
align-items: flex-end;
gap: 0.16rem;
}
.attachment-hint {
font-size: 0.26rem;
color: #999;
}
.upload-icon {
font-size: 0.72rem;
color: #c8c9cc;
}
.file-upload-row{
padding:0px 20px 20px 20px;
.file-upload-label{
font-size: 14px;
color: #333;
margin-bottom: 0.16rem;
}
}
.uploader-wrapper {
:deep(.van-uploader__upload) {
background: #f5f7fa;
border-radius: 0.1rem;
}
}
</style>

76
zdxt-web-client/zdxt-efcode-enforcement-app/src/components/enforcement/register/EnterpriseInfoSection.vue

@ -0,0 +1,76 @@
<template>
<div class="section">
<div class="section-title">企业信息</div>
<!-- 企业名称 -->
<van-field v-model="enterpriseName" :is-link="isSignMode" :readonly="true" name="enterpriseName" label="企业名称"
placeholder="请选择" @click="handleClick" clearable
:rules="isSignMode ? [{ required: true, message: '请选择企业名称' }] : []" :required="isSignMode" />
<!-- 统一社会信用代码 -->
<van-field v-model="enterpriseNumber" :is-link="isSignMode" :readonly="true" name="enterpriseNumber"
label="统一社会信用代码" placeholder="请选择" @click="handleClick" clearable
:rules="isSignMode ? [{ required: true, message: '请选择统一社会信用代码' }] : []" :required="isSignMode" />
</div>
</template>
<script setup name="EnterpriseInfoSection">
import { computed } from 'vue';
const props = defineProps({
enterpriseData: {
type: Object,
default: null
},
isSignMode: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:enterpriseData', 'open-picker']);
// 计算属性:企业名称(支持双向绑定)
const enterpriseName = computed({
get: () => props.enterpriseData?.enterpriseName || '',
set: (val) => {
emit('update:enterpriseData', { ...props.enterpriseData, enterpriseName: val });
}
});
// 计算属性:统一社会信用代码(支持双向绑定)
const enterpriseNumber = computed({
get: () => props.enterpriseData?.enterpriseNumber || '',
set: (val) => {
emit('update:enterpriseData', { ...props.enterpriseData, enterpriseNumber: val });
}
});
const handleClick = () => {
emit('open-picker');
};
</script>
<style lang="less" scoped>
.section {
background: #fff;
border-radius: 0.16rem;
margin-bottom: 0.24rem;
overflow: hidden;
:deep(.van-field__label) {
width: 64px;
margin-right: 22px;
color: #000;
font-size: 14px;
}
}
.section-title {
padding: 0.28rem 0.32rem;
font-size: 16px;
font-weight: 600;
color: #000;
}
</style>

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

@ -0,0 +1,461 @@
<template>
<div class="register-popups">
<!-- 企业选择弹窗 -->
<van-popup v-model:show="localEnterprisePopup" position="bottom" :style="{ height: '80%' }" round>
<div class="enterprise-search-popup">
<div class="popup-header">
<span class="popup-title">选择企业</span>
<van-icon name="cross" class="close-icon" @click="localEnterprisePopup = false" />
</div>
<div class="search-bar">
<van-search v-model="enterpriseSearchKeyword" placeholder="请输入企业名称/统一信用代码搜索"
@search="handleEnterpriseSearch" @clear="clearEnterpriseSearch" />
<van-button class="search-btn" type="primary" @click="handleEnterpriseSearch">搜索</van-button>
</div>
<div class="enterprise-list">
<div v-for="(item, index) in enterpriseList" :key="index" class="enterprise-list-item"
@click="$emit('selectEnterprise', item)">
<div class="enterprise-name">{{ item.enterpriseName }}</div>
<div class="enterprise-number">统一社会信用代码:{{ item.enterpriseNumber }}</div>
</div>
<van-empty v-if="enterpriseList.length === 0" description="暂无数据" />
</div>
</div>
</van-popup>
<!-- 预告查询弹窗 -->
<van-popup v-model:show="localNoticePopup" position="bottom" :style="{ height: '70%' }">
<div class="notice-popup">
<div class="popup-header">
<span class="popup-title">预告查询</span>
<van-icon name="cross" class="close-icon" @click="localNoticePopup = false" />
</div>
<div class="popup-content" @scroll="onNoticeScroll">
<div v-for="(notice, index) in noticeList" :key="index" class="notice-item">
<div class="notice-info">
<div class="notice-matter">执法事项:{{ notice.matter }}</div>
<div class="notice-time">执法时间:{{ notice.time }}</div>
</div>
<van-button class="link-btn" type="primary" @click="$emit('linkNotice', notice)">
一键关联
</van-button>
</div>
<div v-if="loading" class="loading-more">
<van-loading size="small" />
<span>加载中...</span>
</div>
<div v-else-if="finished" class="no-more">没有更多了</div>
</div>
</div>
</van-popup>
<!-- 类别选择弹窗 -->
<van-popup v-model:show="localCategoryPopup" round position="bottom">
<van-picker :columns="options.categories" @cancel="localCategoryPopup = false"
@confirm="$emit('confirmCategory', $event)" />
</van-popup>
<!-- 任务来源弹窗 -->
<van-popup v-model:show="localTaskSourcePopup" round position="bottom">
<van-picker :columns="options.taskSources" @cancel="localTaskSourcePopup = false"
@confirm="$emit('confirmTaskSource', $event)" />
</van-popup>
<!-- 人员查询弹窗 -->
<van-popup v-model:show="localPersonPopup" position="bottom" :style="{ height: '80%' }">
<div class="person-search-popup">
<div class="popup-header">
<span class="popup-title">人员查询</span>
<van-icon name="cross" class="close-icon" @click="localPersonPopup = false" />
</div>
<div class="search-bar">
<van-search v-model="personSearchKeyword" placeholder="请输入姓名/执法证件号搜索" @search="handlePersonSearch"
@clear="clearPersonSearch" />
<van-button class="search-btn" type="primary" @click="handlePersonSearch">搜索</van-button>
</div>
<div class="person-list">
<div v-for="(person, index) in personList" :key="index" class="person-list-item"
@click="$emit('selectPerson', person)">
<div class="person-name">{{ person.name }}</div>
<div class="person-id">执法证件号:{{ person.id }}</div>
</div>
<van-empty v-if="personList.length === 0" description="暂无数据" />
</div>
</div>
</van-popup>
<!-- 执法依据弹窗 -->
<van-popup v-model:show="localBasisPopup" position="bottom" :style="{ height: '80%' }">
<div class="enforcement-basis-popup">
<div class="popup-header">
<span class="popup-title">执法依据</span>
<van-icon name="cross" class="close-icon" @click="localBasisPopup = false" />
</div>
<div class="search-bar">
<van-search v-model="basisSearchKeyword" placeholder="请输入关键字搜索" @search="handleBasisSearch"
@clear="clearBasisSearch" />
<van-button class="search-btn" type="primary" @click="handleBasisSearch">搜索</van-button>
</div>
<div class="enforcement-basis-list">
<div v-for="(item, index) in enforcementBasisList" :key="index" class="basis-list-item"
@click="$emit('selectBasis', item)">
<div class="basis-name">{{ item.name }}</div>
</div>
<van-empty v-if="enforcementBasisList.length === 0" description="没有更多了" />
</div>
</div>
</van-popup>
</div>
</template>
<script setup name="RegisterPopups">
import { ref, reactive, watch, computed } from 'vue';
const props = defineProps({
visible: {
type: Object,
required: true
},
options: {
type: Object,
default: () => ({
categories: [],
taskSources: []
})
}
});
const emit = defineEmits([
'close-popup',
'selectEnterprise',
'linkNotice',
'confirmCategory',
'confirmTaskSource',
'selectPerson',
'selectBasis'
]);
// 使用 computed 创建可写的计算属性(自动同步 props,保证响应式)
const localEnterprisePopup = computed({
get: () => props.visible.enterprise,
set: () => emit('close-popup', 'enterprise')
});
const localNoticePopup = computed({
get: () => props.visible.notice,
set: () => emit('close-popup', 'notice')
});
const localCategoryPopup = computed({
get: () => props.visible.category,
set: () => emit('close-popup', 'category')
});
const localTaskSourcePopup = computed({
get: () => props.visible.taskSource,
set: () => emit('close-popup', 'taskSource')
});
const localPersonPopup = computed({
get: () => props.visible.person,
set: () => emit('close-popup', 'person')
});
const localBasisPopup = computed({
get: () => props.visible.basis,
set: () => emit('close-popup', 'basis')
});
// ========== 企业选择相关 ==========
const enterpriseSearchKeyword = ref('');
const enterpriseList = ref([]);
const initEnterpriseList = () => {
enterpriseList.value = [
{ enterpriseName: '深圳市南山区椿立嘉源麻辣烫店', enterpriseNumber: '92440300MA5FXXXX1' },
{ enterpriseName: '深圳伊腾迪新能源有限公司', enterpriseNumber: '91440300MA5FXXXX2' },
{ enterpriseName: '深圳市永顺新绣花有限公司', enterpriseNumber: '91440300MA5FXXXX3' },
{ enterpriseName: '深圳市中棋金业有限公司', enterpriseNumber: '91440300MA5FXXXX4' },
{ enterpriseName: '深圳市百合隆科技有限公司', enterpriseNumber: '91440300MA5FXXXX5' },
];
};
const handleEnterpriseSearch = () => {
initEnterpriseList();
if (enterpriseSearchKeyword.value.trim()) {
const keyword = enterpriseSearchKeyword.value.toLowerCase();
enterpriseList.value = enterpriseList.value.filter(item =>
item.enterpriseName.toLowerCase().includes(keyword) ||
item.enterpriseNumber.toLowerCase().includes(keyword)
);
}
};
const clearEnterpriseSearch = () => {
enterpriseSearchKeyword.value = '';
initEnterpriseList();
};
watch(localEnterprisePopup, (val) => {
if (val) {
enterpriseSearchKeyword.value = '';
initEnterpriseList();
}
});
// ========== 人员查询相关 ==========
const personSearchKeyword = ref('');
const personList = ref([]);
const initPersonList = () => {
personList.value = [
{ id: '19020011041', name: '刘倩琳' },
{ id: '19020011042', name: '张明华' },
{ id: '19020011043', name: '李晓东' },
{ id: '19020011044', name: '王芳' },
];
};
const handlePersonSearch = () => {
initPersonList();
if (personSearchKeyword.value.trim()) {
const keyword = personSearchKeyword.value.toLowerCase();
personList.value = personList.value.filter(item =>
item.name.toLowerCase().includes(keyword) ||
item.id.toLowerCase().includes(keyword)
);
}
};
const clearPersonSearch = () => {
personSearchKeyword.value = '';
initPersonList();
};
watch(localPersonPopup, (val) => {
if (val) {
personSearchKeyword.value = '';
initPersonList();
}
});
// ========== 执法依据相关 ==========
const basisSearchKeyword = ref('');
const enforcementBasisList = ref([]);
const initBasisList = () => {
enforcementBasisList.value = [
{ id: 1, name: '《中华人民共和国行政处罚法》' },
{ id: 2, name: '《中华人民共和国行政许可法》' },
{ id: 3, name: '《中华人民共和国行政强制法》' },
{ id: 4, name: '《深圳市经济特区商事登记若干规定》' },
{ id: 5, name: '《深圳经济特区优化营商环境条例》' },
{ id: 6, name: '《中华人民共和国市场主体登记管理条例》' },
];
};
const handleBasisSearch = () => {
initBasisList();
if (basisSearchKeyword.value.trim()) {
const keyword = basisSearchKeyword.value.toLowerCase();
enforcementBasisList.value = enforcementBasisList.value.filter(item =>
item.name.toLowerCase().includes(keyword)
);
}
};
const clearBasisSearch = () => {
basisSearchKeyword.value = '';
initBasisList();
};
watch(localBasisPopup, (val) => {
if (val) {
basisSearchKeyword.value = '';
initBasisList();
}
});
// ========== 预告查询相关 ==========
const noticeList = ref([]);
const noticePage = ref(1);
const loading = ref(false);
const finished = ref(false);
const loadNotices = () => {
if (loading.value || finished.value) return;
loading.value = true;
setTimeout(() => {
const mockData = [
{ id: 1, matter: '例行检查', time: '2026-05-20 10:00' },
{ id: 2, matter: '专项检查', time: '2026-05-19 14:30' },
];
if (noticePage.value === 1) {
noticeList.value = mockData;
} else {
noticeList.value = [...noticeList.value, ...mockData];
}
loading.value = false;
if (noticePage.value >= 1) {
finished.value = true;
}
noticePage.value++;
}, 500);
};
const onNoticeScroll = () => { };
watch(localNoticePopup, (newVal) => {
if (newVal) {
noticePage.value = 1;
noticeList.value = [];
loading.value = false;
finished.value = false;
loadNotices();
}
});
</script>
<style lang="less" scoped>
.register-popups {
// 容器样式(可选)
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.3rem;
border-bottom: 1px solid #f5f5f5;
.popup-title {
font-size: 0.4rem;
font-weight: 600;
color: #333;
}
.close-icon {
font-size: 0.45rem;
color: #999;
}
}
.search-bar {
display: flex;
align-items: center;
margin: 0.3rem;
// padding: 0.3rem;
:deep(.van-search) {
padding-right: 0px;
flex: 1;
height: 34px;
}
.search-btn {
flex: none;
height: 34px;
}
}
.enterprise-search-popup,
.person-search-popup,
.enforcement-basis-popup {
height: 100%;
display: flex;
flex-direction: column;
}
.enterprise-list,
.person-list,
.enforcement-basis-list {
flex: 1;
overflow-y: auto;
padding: 0 0.3rem;
}
.enterprise-list-item,
.person-list-item,
.basis-list-item {
padding:20px 20px 14px 14px;
border-bottom: 1px solid #f5f5f5;
&:active {
background: #f9f9f9;
}
}
.enterprise-name,
.person-name,
.basis-name {
font-size: 16px;
color: #333;
margin-bottom: 10px;
}
.enterprise-number,
.person-id {
font-size: 14px;
color: #999;
}
.notice-popup {
height: 100%;
display: flex;
flex-direction: column;
}
.popup-content {
flex: 1;
overflow-y: auto;
padding: 0.3rem;
}
.notice-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.3rem;
// background: #f9f9f9;
margin-bottom: 0.2rem;
border-bottom: 0.1px solid #ededed;
.notice-info {
flex: 1;
}
.notice-matter {
font-size: 16px;
color: #000;
margin-bottom: 10px;
}
.notice-time {
font-size: 14px;
color: #999;
}
.link-btn {
flex: none;
font-size: 12px;
height: 26px;
}
}
.loading-more,
.no-more {
text-align: center;
padding: 0.3rem;
font-size: 0.28rem;
color: #999;
display: flex;
align-items: center;
justify-content: center;
gap: 0.1rem;
}
</style>

2
zdxt-web-client/zdxt-efcode-enforcement-app/src/router/enforcement-app/enf-main-router.js

@ -83,7 +83,7 @@ const routes = [
},
// 执法端-登记页面
{
path: "/enforcement/register",
path: "/enforcement/enforcementRegister",
name: "enforcementRegister",
component: () => import("@/views/enforcement/enforce/enforcementRegister.vue"),
meta: {

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

File diff suppressed because it is too large

226
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enterpriseScanPreview.vue

@ -1,20 +1,20 @@
<template>
<div class="home app_content">
<div class="home">
<!-- 中间 -->
<div class="warp_body">
<div class="white-body">
<!-- Tab 切换 -->
<div class="tabs">
<div class="tab-item" :class="{ active: currentTab === 0 }" @click="currentTab = 0">
<span>企业画像</span>
<div class="tab-item portrayal" :class="{ active: currentTab === 0 }" @click="currentTab = 0">
<div class="tab-item-title">企业画像</div>
</div>
<div class="tab-item" :class="{ active: currentTab === 1 }" @click="currentTab = 1">
<span>执法记录</span>
<div class="tab-item record" :class="{ active: currentTab === 1 }" @click="currentTab = 1">
<div class="tab-item-title">执法记录</div>
</div>
</div>
<!-- 企业画像 Tab -->
<div v-if="currentTab === 0" class="tab-content">
<div v-if="currentTab === 0">
<!-- 企业信息卡片 -->
<div class="enterprise-card">
<div v-if="enterpriseData" class="enterprise-info">
@ -51,14 +51,16 @@
<div class="info-label">注册地址</div>
<div class="info-value address-value">
<span class="address-text">{{ enterpriseData.enterpriseAddress }}</span>
<span class="expand-text" v-if="enterpriseData.enterpriseAddress && enterpriseData.enterpriseAddress.length > 20">展开</span>
<span class="expand-text"
v-if="enterpriseData.enterpriseAddress && enterpriseData.enterpriseAddress.length > 20">展开</span>
</div>
</div>
<div class="info-item">
<div class="info-label">经营范围</div>
<div class="info-value scope-value">
<span class="scope-text">{{ enterpriseData.businessScope }}</span>
<span class="expand-text" v-if="enterpriseData.businessScope && enterpriseData.businessScope.length > 30">展开</span>
<span class="expand-text"
v-if="enterpriseData.businessScope && enterpriseData.businessScope.length > 30">展开</span>
</div>
</div>
</template>
@ -76,23 +78,6 @@
<van-icon :name="isCollapsed ? 'arrow-down' : 'arrow-up'" />
</div>
</div>
<!-- 执法记录卡片 -->
<div class="record-card">
<!-- 执法记录子Tab -->
<div class="record-tabs">
<div class="record-tab-item" :class="{ active: recordTab === 0 }" @click="recordTab = 0">
<span>历史执法案件</span>
</div>
<div class="record-tab-item" :class="{ active: recordTab === 1 }" @click="recordTab = 1">
<span>历史处罚案件</span>
</div>
<div class="record-tab-item" :class="{ active: recordTab === 2 }" @click="recordTab = 2">
<span>历史强制案件</span>
</div>
</div>
<van-empty description="暂无执法记录" />
</div>
</div>
<!-- 执法记录 Tab -->
@ -109,26 +94,19 @@
<!-- 执法记录列表 -->
<div class="record-list">
<div
v-for="(item, index) in (recordTab === 0 ? cityRecords : unitRecords)"
:key="index"
class="record-card-item"
>
<div v-for="(item, index) in (recordTab === 0 ? cityRecords : unitRecords)" :key="index"
class="record-card-item">
<div class="record-header">
<div class="record-date">{{ item.date }}</div>
<div class="record-company">{{ enterpriseData?.enterpriseName }}</div>
<div class="record-count">被执法次数:{{ item.count }}次</div>
</div>
<div class="record-timeline" v-if="isRecordExpanded">
<div
v-for="(timeline, tIndex) in item.timeline"
:key="tIndex"
class="timeline-item"
>
<div v-for="(timeline, tIndex) in item.timeline" :key="tIndex" class="timeline-item" :class="{ 'has-line': tIndex !== item.timeline.length - 1 }">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-time">{{ timeline.time }}</div>
<div class="timeline-unit">执法单位:{{ timeline.unit }}</div>
<div class="timeline-unit"><span class="unit-label">执法单位:</span>{{ timeline.unit }}</div>
</div>
</div>
</div>
@ -142,6 +120,23 @@
</div>
</div>
</div>
</div>
<!-- 执法记录卡片 -->
<div class="record-card" v-if="currentTab === 0">
<!-- 执法记录子Tab -->
<div class="record-tabs">
<div class="record-tab-item" :class="{ active: recordTab === 0 }" @click="recordTab = 0">
<span>历史执法案件</span>
</div>
<div class="record-tab-item" :class="{ active: recordTab === 1 }" @click="recordTab = 1">
<span>历史处罚案件</span>
</div>
<div class="record-tab-item" :class="{ active: recordTab === 2 }" @click="recordTab = 2">
<span>历史强制案件</span>
</div>
</div>
<van-empty description="暂无执法记录" />
</div>
</div>
@ -232,7 +227,7 @@ const formatDate = (dateStr) => {
// ========== 页面跳转 ==========
const toSign = () => {
router.push({
path: '/enforcement/register',
path: '/enforcement/enforcementRegister',
});
};
</script>
@ -255,49 +250,65 @@ const toSign = () => {
.white-body {
margin: 0 0.3rem;
width: calc(100% - 0.6rem);
min-height: 100vh;
background-color: #fff;
border-radius: 0.27rem;
overflow: hidden;
margin-bottom: 20px;
}
}
.tabs {
display: flex;
padding: 0.3rem 0.3rem 0;
gap: 0.1rem;
}
.tab-item {
flex: 1;
height: 0.9rem;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
transform: skewX(-15deg);
span {
display: block;
width: 100%;
height: 100%;
line-height: 0.9rem;
text-align: center;
.tab-item-title {
font-size: 0.38rem;
color: #1367fe;
background: #e6f0fd;
transform: skewX(15deg);
}
&.active span {
background: linear-gradient(135deg, #3b82f6, #2563eb);
color: #fff;
&.portrayal {
background: url('@/assets/images/tab_bg1_1.png') no-repeat center center;
background-size: 100% 100%;
.tab-item-title {
color: #1989fa;
}
&.active {
background: url('@/assets/images/tab_bg1.png') no-repeat center center;
background-size: 100% 100%;
.tab-item-title {
color: #fff;
}
}
}
}
.tab-content {
padding: 0.3rem;
&.record {
background: url('@/assets/images/tab_bg.png') no-repeat center center;
background-size: 100% 100%;
.tab-item-title {
color: #1989fa;
}
&.active {
background: url('@/assets/images/tab_bg_1.png') no-repeat center center;
background-size: 100% 100%;
.tab-item-title {
color: #fff;
}
}
}
}
.enterprise-card {
@ -320,10 +331,11 @@ const toSign = () => {
.info-label {
flex: none;
width: 2.2rem;
font-size: 0.35rem;
color: #333;
width: 74px;
font-size: 14px;
color: #000;
line-height: 1.5;
margin-right: 30px;
}
.info-value {
@ -367,6 +379,13 @@ const toSign = () => {
background: #fff;
border-radius: 0.2rem;
padding: 0 0.3rem;
margin: 0.3rem;
}
.tab-content {
height: calc(100vh - 70px);
background: #fff;
box-sizing: border-box;
}
.record-tabs {
@ -382,26 +401,18 @@ const toSign = () => {
cursor: pointer;
position: relative;
span {
font-size: 0.35rem;
color: #999;
}
&.active {
border-bottom: 3px solid #1989fa;
&.active span {
color: #1367fe;
font-weight: 500;
span {
color: #1989fa;
font-weight: 650;
}
}
&.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 1.5rem;
height: 0.06rem;
background: #1367fe;
border-radius: 0.03rem;
span {
font-size: 0.35rem;
color: #999;
}
}
@ -423,53 +434,54 @@ const toSign = () => {
color: #999;
}
}
.record-list {
margin-top: 0.3rem;
}
.record-card-item {
background: #fff;
border-radius: 0.2rem;
border-radius: 8px;
padding: 0.3rem;
margin-bottom: 0.3rem;
margin: 10px;
margin-top: 20px;
box-shadow: 0 0px 8px #e5e5e5;
}
.record-header {
margin-bottom: 0.3rem;
.record-date {
font-size: 0.32rem;
font-size: 14px;
color: #999;
margin-bottom: 0.15rem;
}
.record-company {
font-size: 0.38rem;
color: #333;
font-weight: 500;
margin-bottom: 0.15rem;
font-size: 16px;
color: #000;
font-weight: 600;
padding: 10px 0px 10px 20px;
letter-spacing: 0.05rem;
}
.record-count {
font-size: 0.32rem;
color: #666;
font-size: 14px;
color: #393939dd;
padding-left: 20px;
letter-spacing: 0.02rem;
}
}
.record-timeline {
position: relative;
padding-left: 0.4rem;
margin-top: 0.2rem;
margin-left: 28px;
margin-top: 26px;
}
.record-timeline::before {
.timeline-item.has-line::before {
content: '';
position: absolute;
left: 0.08rem;
top: 0;
left: 3px;
top: 20px;
bottom: 0;
width: 1px;
width: 2px;
height: 48px;
background: #e5e5e5;
}
@ -480,14 +492,15 @@ const toSign = () => {
position: relative;
.timeline-dot {
width: 0.16rem;
height: 0.16rem;
width: 10px;
height: 10px;
border-radius: 50%;
background: #1367fe;
margin-right: 0.2rem;
flex-shrink: 0;
position: relative;
z-index: 1;
top: 4px;
}
.timeline-content {
@ -495,18 +508,19 @@ const toSign = () => {
min-width: 0;
.timeline-time {
font-size: 0.32rem;
color: #333;
font-size: 16px;
color: #ccc;
font-weight: 400;
line-height: 1.4;
}
.timeline-unit {
font-size: 0.28rem;
font-size: 14px;
color: #999;
margin-top: 0.04rem;
padding-left: 0.4rem;
line-height: 1.4;
padding:10px 0px;
.unit-label{
color: #000;
}
}
}
}

14
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/scanEntQRCode.vue

@ -46,7 +46,6 @@
v-model:show="showSearchModal"
position="bottom"
:style="{ height: '80%' }"
round
closeable
close-icon="cross"
:close-on-click-overlay="false"
@ -80,7 +79,6 @@
type="text"
class="search-input"
placeholder="请输入企业名称/统一信用代码"
@keyup.enter="handleSearch"
/>
<van-icon
v-if="searchKeyword"
@ -369,6 +367,7 @@ const clearSearch = () => {
enterpriseList.value = [];
finished.value = false;
error.value = false;
loadEnterprises();
};
const loadEnterprises = () => {
@ -495,7 +494,7 @@ const selectEnterprise = (item) => {
const toSign = () => {
router.push({
path: '/enforcement/register',
path: '/enforcement/enforcementRegister',
query: {
reason: '补录',
checkCss: 9,
@ -651,14 +650,14 @@ onBeforeUnmount(() => {
:deep(.van-popup__close-icon--top-right) {
top: 0.3rem;
right: 0.3rem;
color: #999;
color: #000;
font-size: 0.5rem;
z-index: 10;
}
.section-label {
font-size: 0.35rem;
color: #333;
color: #000;
margin-bottom: 0.25rem;
font-weight: 500;
}
@ -755,7 +754,7 @@ onBeforeUnmount(() => {
.enterprise-item {
display: flex;
align-items: center;
// align-items: center;
padding: 0.3rem 0;
border-bottom: 1px solid #f5f5f5;
cursor: pointer;
@ -785,8 +784,9 @@ onBeforeUnmount(() => {
.item-name {
flex: 1;
font-size: 0.35rem;
color: #333;
color: #000;
line-height: 1.4;
margin-top: 5px;
}
.loading-more {

Loading…
Cancel
Save