This commit is contained in:
2025-06-19 17:33:18 +08:00
commit f8f31dc4d9
437 changed files with 103080 additions and 0 deletions
@@ -0,0 +1,10 @@
{
"usingComponents": {
"van-picker": "@vant/weapp/picker/index",
"van-popup": "@vant/weapp/popup/index",
"van-datetime-picker": "@vant/weapp/datetime-picker/index",
"van-search": "@vant/weapp/search/index",
"van-dialog": "@vant/weapp/dialog/index"
},
"navigationBarTitleText": "违章上报"
}
@@ -0,0 +1,148 @@
/* pages/violationReporting/violationReporting.wxss */
page{
background-color: #EFF1F4;
}
.identificationBox{
background-color: #ffffff;
padding:16rpx 0;
width: 100%;
padding: 16rpx 0;
position: relative;
.textarea{
height: 176rpx;
width: calc(100% - 56rpx);
border-radius: 16rpx;
border: 2rpx solid #E6EAF2;
margin: 0 16rpx 16rpx 16rpx;
padding: 12rpx;
font-size: 28rpx;
}
.analysisBtn{
position: relative;
width: calc(100% - 32rpx);
margin: 0 16rpx;
.tips{
color:red;
font-size: 25rpx;
}
}
.button{
width: 128rpx;
height: 64rpx;
background: linear-gradient(90deg, #3B7CFC 0%, #548BFC 100%);
border-radius: 16rpx;
text-align: center;
line-height: 64rpx;
color: #fff;
// position: absolute;
// right: 16px;
&.disabled{
opacity: 0.35;
}
}
}
.flex-between{
display: flex;
align-items: center;
justify-content: space-between;
}
.flex-align{
display:flex;
align-items: center;
}
.flex-end{
display: flex;
align-items: center;
justify-content: flex-end;
}
.box{
background-color: #ffffff;
font-size: 28rpx;
padding:28rpx 20rpx 0 20rpx;
margin-top: 16rpx;
.row{
.left{
color: #333F53;
font-weight: bold;
.bg{
width: 8rpx;
height: 44rpx;
background: linear-gradient(180deg, #4E87FC 0%, #38B1FE 100%);
border-radius: 4rpx;
margin-right: 16rpx;
}
}
.right{
color: #2E3A4F;
}
}
.info-item{
padding: 20rpx;
border-bottom: 2rpx solid #E6EAF2;
&.borderBottomNone{
border-bottom: none;
}
&.disabledItem{
background: #EFF1F4;
box-shadow: 0px 1px 0px 0px #E6EAF2;
.right{
color: #949CB5;
}
}
.left{
color: #2F3B50;
width:40%;
.require{
color: #EA4955;
}
}
.right{
width: 60%;
text-align: right;
&.selectItem{
width: 100%;
justify-content: flex-end;
.placeholder{
color: #949CB5;
}
}
.arrow{
width: 0;
height: 0;
border-left: 13rpx solid transparent;
border-right: 13rpx solid transparent;
border-top: 15rpx solid #636B83;
margin-left: 18rpx;
}
}
.tips{
color:red;
text-align: right;
margin-top: 10rpx;
font-size: 25rpx;
}
}
}
.searchBox{
position: absolute;
width: 100%;
left: 0;
top: 80rpx;
z-index: 63;
}
.operateBtn{
box-shadow: 0rpx -4rpx 24rpx 0rpx rgba(0,0,0,0.2);
border-radius: 16rpx 16rpx 0rpx 0rpx;
padding: 45rpx 22rpx;
font-size: 30rpx;
margin-top: 40rpx;
background-color: #ffffff;
.submitBtn{
height: 80rpx;
background: linear-gradient(90deg, #3B7CFC 0%, #548BFC 100%);
border-radius: 16rpx;
color: #FFFFFF;
line-height: 80rpx;
text-align: center;
}
}
@@ -0,0 +1,385 @@
// pages/violationReporting/violationReporting.ts
import { getAction, upload, postAction } from '../../api/base';
import Dialog from '@vant/weapp/dialog/dialog';
const dayjs = require('dayjs');
const isSpecialChar: RegExp = /[\"'{};<>!@#$%^&*|\\\\]/; //特殊字符校验
const moneyNumberExp: RegExp = /^\d{1,9}(\.\d{1,2})?$/; //金额限制整数部分不能大于9位 小数部分不能大于2位
Page({
/**
* 页面的初始数据
*/
data: {
identificationVal: '',
allVehicleList: [], //车辆列表
violationBehavior: [], //违章行为列表
violationTimestamp: new Date().getTime(),
requireObj: {
plateNumber: '',
projectId: '',
director: '',
violationTime: '',
violationAddress: '',
violationActText: ''
},
params: {
plateNumber: '',
projectId: '', //所属项目id
director: '', //责任人
violationTime: '', //违章时间
violationAddress: '', //违章地点
violationAct: '', //违章行为
violationActText: '',
violationActDes: '', //违章行为描述
violationDriver: '', //违章司机
violationScore: '', //违章计分
violationCost: '', //违章费用
handleTerm: '', //处理期限(天)
handleTime: '', //处理时间
handleResult: '', //处理结果(1-已处理、0-未处理)
handleVoucher: '', //处理凭证(附件url
remark: '' //备注
},
validate: {
message: ''
},
plateNumberShow: false,
violationTimeShow: false,
violationActShow: false,
plateNumberSearchVal: '',
violationActDefaultIdx: 0, //违章行为默认值
plateNumberPickerIndex: 0 //车牌号码默认值
},
inputChange(event: any) {
//获取输入框输入内容
let fieldName: any = event.currentTarget.dataset.fieldname;
let data: any = { ...this.data };
data.params[fieldName] = event.detail.value;
if (
fieldName === 'message' ||
fieldName === 'violationActDes' ||
fieldName === 'violationDriver' ||
fieldName === 'violationScore' ||
fieldName === 'violationCost' ||
fieldName === 'handleTerm'
) {
if (event.detail.value) {
if (isSpecialChar.test(event.detail.value)) {
//非必填特殊符号校验
data.validate[fieldName] = '内容含有特殊字符';
} else if (
fieldName === 'violationCost' &&
Number(event.detail.value) > 999999
) {
data.validate[fieldName] = '违章费用不能大于999999';
} else if (
(fieldName === 'violationScore' || fieldName === 'handleTerm') &&
Number(event.detail.value) > 32766
) {
data.validate[fieldName] =
(fieldName === 'violationScore' ? '违章计分' : '处理期限') +
'不能大于32766';
} else {
data.validate[fieldName] = '';
delete data.validate[fieldName];
}
} else {
data.validate[fieldName] = '';
delete data.validate[fieldName];
}
}
if (data.params[fieldName]) {
if (isSpecialChar.test(data.params[fieldName])) {
data.requireObj[fieldName] = '内容含有特殊字符';
} else {
delete data.requireObj[fieldName];
}
} else {
data.requireObj[fieldName] = '此项为必填项';
}
this.setData(data);
},
pickerChange(event: any) {
//下拉框切换是否展示
let fieldName: any = event.currentTarget.dataset.fieldname;
let data: any = { ...this.data };
data[fieldName] = true;
this.setData(data);
},
hidePicker() {
this.setData({
plateNumberShow: false,
violationTimeShow: false,
violationActShow: false
});
},
pickerConfirm(event: any) {
//下拉框点击确认
let { fieldname, fieldvalue } = event.currentTarget.dataset;
let detail = event.detail;
let data: any = { ...this.data };
if (fieldname === 'plateNumber') {
data.params.projectId = detail.value.projectId;
data.params.projectName = detail.value.projectName;
data.params.director = detail.value.projectManager;
data.params[fieldname] = detail.value.text;
if (data.params.projectId) {
delete data.requireObj.projectId;
} else {
data.requireObj.projectId = '此项为必填项';
}
if (data.params.director) {
delete data.requireObj.director;
} else {
data.requireObj.director = '此项为必填项';
}
} else if (fieldname === 'violationTime') {
data.params[fieldname] = dayjs(detail).format('YYYY-MM-DD HH:mm');
data.violationTimestamp = detail;
} else {
data.params[fieldname] = detail.value.text;
data.params[fieldvalue] = detail.value.value;
}
if (data.params[fieldname]) {
delete data.requireObj[fieldname];
} else {
data.requireObj[fieldname] = '此项为必填项';
}
this.setData(data);
this.hidePicker();
},
queryAllVehicle(event: any) {
getAction('api/vehicles/violation/queryVehicleProject', {
plateNumber: event ? event.detail : ''
}).then((res: any) => {
let list = res.result;
let arr: any = [];
list.map((item: any) => {
let obj: any = {
value: item.plateNumber,
text: item.plateNumber,
projectId: item.projectId,
projectName: item.projectName,
projectManager: item.projectManager
};
arr.push(obj);
});
this.setData({
allVehicleList: arr,
plateNumberSearchVal: event ? event.detail : '',
plateNumberPickerIndex: 0
});
console.log('车牌号码列表', this.data.allVehicleList);
});
},
getDict() {
getAction('api/sys/dict/getDictItems/violation_behavior').then(
(res: any) => {
this.setData({
violationBehavior: res.result
});
console.log('违章行为列表', this.data.violationBehavior);
}
);
},
analyzeMessage() {
let params: any = this.data.params;
if (!params.message || this.data.validate.message) {
return;
}
postAction('api/vehicles/violation/analyzeMessage', {
template: params.message
}).then((res: any) => {
let result: any = res.result;
let requireObj: any = this.data.requireObj;
let busList: any = this.data.allVehicleList;
let violationBehavior: any = this.data.violationBehavior; //违章行为列表
let date = result.violationTime
? dayjs(dayjs(result.violationTime, 'YYYY-MM-DD HH:mm')).format(
'YYYY-MM-DD HH:mm'
)
: dayjs().format('YYYY-MM-DD HH:mm');
let busFilterIdx =
busList.length > 0
? busList.findIndex((x: any) => x.text.includes(result.plateNumber))
: [];
result.director =
busFilterIdx != -1 ? busList[busFilterIdx].projectManager : null;
result.projectId =
busFilterIdx != -1 ? busList[busFilterIdx].projectId : null;
result.projectName =
busFilterIdx != -1 ? busList[busFilterIdx].projectName : null;
let violationActDefaultIdx, plateNumberPickerIndex;
plateNumberPickerIndex = busFilterIdx != -1 ? busFilterIdx : 0;
if (result.violationAct_dictText) {
let behaviorFilterIndex =
violationBehavior.length > 0
? violationBehavior.findIndex((x: any) =>
x.text.includes(result.violationAct_dictText)
)
: [];
result.violationAct_dictText =
behaviorFilterIndex != -1 ? result.violationAct_dictText : null;
violationActDefaultIdx =
behaviorFilterIndex != -1 ? behaviorFilterIndex : 0;
} else {
violationActDefaultIdx = 0;
}
// 新的违章行为
if (result.isNewBehavior) {
Dialog.confirm({
title: '违章行为',
message: `识别出新的违章行为『${result.newBehavior}』,是否更新?`
})
.then(() => {
postAction('api/vehicles/violation/addViolationBehavior', {
newBehavior: result.newBehavior
}).then((data: any) => {
if (data.success) {
this.getDict();
delete requireObj.violationActText;
this.setData({
[`params.violationAct`]: data.message,
[`params.violationActText`]: result.newBehavior,
requireObj
});
} else {
wx.showToast(data.message);
}
});
})
.catch(() => {});
}
if (result.plateNumber) {
delete requireObj.plateNumber;
} else {
requireObj.projectId = '此项为必填项';
}
if (result.violationAct_dictText) {
delete requireObj.violationActText;
} else {
requireObj.violationActText = '此项为必填项';
}
if (result.violationTime) {
delete requireObj.violationTime;
} else {
requireObj.violationTime = '此项为必填项';
}
if (result.violationAddress) {
delete requireObj.violationAddress;
} else {
requireObj.violationAddress = '此项为必填项';
}
if (result.director) {
delete requireObj.director;
} else {
requireObj.director = '此项为必填项';
}
if (result.projectId) {
delete requireObj.projectId;
} else {
requireObj.projectId = '此项为必填项';
}
this.setData({
[`params.plateNumber`]: result.plateNumber,
// [`params.violationAct`]: result.violationAct,
// [`params.violationActText`]: result.violationAct_dictText,
[`params.violationAddress`]: result.violationAddress,
[`params.director`]: result.director,
[`params.projectId`]: result.projectId,
[`params.projectName`]: result.projectName,
[`params.violationTime`]: date,
[`violationTimestamp`]: date
? dayjs(date).valueOf()
: new Date().getTime(),
requireObj,
violationActDefaultIdx,
plateNumberPickerIndex
});
console.log('识别内容', res);
});
},
formSubmit() {
//提交
let isRequireOk: any = true;
let params: any = this.data.params;
let requireObj: any = this.data.requireObj;
let validate: any = this.data.validate;
for (let key in requireObj) {
if (!requireObj[key] || requireObj[key] === '此项为必填项') {
isRequireOk = false;
requireObj[key] = '此项为必填项';
}
}
for (let key in validate) {
if (validate[key]) {
isRequireOk = false;
}
}
this.setData({
requireObj
});
if (!isRequireOk) {
return;
}
params.violationScore = Number(params.violationScore);
params.violationCost = Number(params.violationCost);
params.handleTerm = Number(params.handleTerm);
console.log('提交参数', params);
postAction('api/vehicles/violation/add', params).then((res: any) => {
wx.showToast({
title: res.message,
icon: 'none',
duration: 1000
});
setTimeout(() => {
wx.reLaunch({
url: '/pages/maintenanceIndex/maintenanceIndex'
});
}, 1000);
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad() {
this.queryAllVehicle(null);
this.getDict();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {},
/**
* 生命周期函数--监听页面显示
*/
onShow() {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {}
});
@@ -0,0 +1,178 @@
<!--pages/violationReporting/violationReporting.wxml-->
<view>
<view class="identificationBox">
<textarea data-fieldname="message" bind:input="inputChange" value="{{params.message}}" class="textarea" placeholder="请粘贴或输入违章短信文本,点击“识别”自动识别违章信息" placeholder-style="color: #949CB5;font-size:28rpx;"></textarea>
<view class="{{validate.message ? 'flex-between' : 'flex-end'}} analysisBtn">
<view class="tips" wx:if="{{validate.message}}">{{validate.message}}</view>
<view class="button {{!params.message || validate.message ? 'disabled' : ''}}" catch:tap="analyzeMessage">识别</view>
</view>
</view>
<view class="box">
<view class="row flex-between">
<view class="left flex-align">
<view class="bg"></view>
<view>基本信息</view>
</view>
</view>
<view class="info-item">
<view class="flex-between">
<view class="left flex-align">
<view class="require">*</view>
<view>车牌号码</view>
</view>
<view class="right flex-align selectItem" data-fieldName="plateNumberShow" catch:tap="pickerChange">
<view wx:if="{{params.plateNumber}}">{{params.plateNumber}}</view>
<view class="placeholder" wx:else>请选择</view>
<view class="arrow"></view>
</view>
</view>
<view class="tips" wx:if="{{requireObj.plateNumber}}">{{requireObj.plateNumber}}</view>
</view>
<!-- 车牌号码下拉选择 -->
<van-popup show="{{plateNumberShow}}" position="bottom" bind:close="hidePicker">
<view class="searchBox">
<van-search value="{{ plateNumberSearchVal }}" placeholder="请输入车牌号" bind:search="queryAllVehicle" />
</view>
<van-picker default-index="{{plateNumberPickerIndex}}" data-fieldname="plateNumber" show-toolbar columns="{{allVehicleList}}" bind:confirm="pickerConfirm" bind:cancel="hidePicker" visible-item-count="8">
</van-picker>
</van-popup>
<view class="info-item disabledItem">
<view class="flex-between">
<view class="left flex-align">
<!-- <view class="require">*</view> -->
<view>所属项目</view>
</view>
<view class="right">
<input value="{{params.projectName}}" type="text" style="width:100%;" disabled />
</view>
</view>
<view class="tips" wx:if="{{requireObj.projectId}}">{{requireObj.projectId}}</view>
</view>
<view class="info-item borderBottomNone">
<view class="flex-between">
<view class="left flex-align">
<view class="require">*</view>
<view>责任人</view>
</view>
<view class="right">
<input placeholder-style="color:#949CB5" data-fieldName="director" bindinput="inputChange" value="{{params.director}}" type="text" placeholder="请输入" style="width:100%;" />
</view>
</view>
<view class="tips" wx:if="{{requireObj.director}}">{{requireObj.director}}</view>
</view>
</view>
<view class="box">
<view class="row flex-between">
<view class="left flex-align">
<view class="bg"></view>
<view>违章信息</view>
</view>
</view>
<view class="info-item">
<view class="flex-between">
<view class="left flex-align">
<view class="require">*</view>
<view>违章时间</view>
</view>
<view class="right flex-align selectItem" data-fieldName="violationTimeShow" catch:tap="pickerChange">
<view wx:if="{{params.violationTime}}">{{params.violationTime}}</view>
<view class="placeholder" wx:else>请选择</view>
<view class="arrow"></view>
</view>
</view>
<view class="tips" wx:if="{{requireObj.violationTime}}">{{requireObj.violationTime}}</view>
</view>
<van-popup show="{{violationTimeShow}}" position="bottom" bind:close="hidePicker">
<van-datetime-picker data-fieldname="violationTime" value="{{ violationTimestamp }}" bind:cancel="hidePicker" bind:confirm="pickerConfirm" />
</van-popup>
<view class="info-item">
<view class="flex-between">
<view class="left flex-align">
<view class="require">*</view>
<view>违章地点</view>
</view>
<view class="right">
<textarea auto-height placeholder-style="color:#949CB5" data-fieldName="violationAddress" bindinput="inputChange" value="{{params.violationAddress}}" type="text" placeholder="请输入" style="width:100%;"></textarea>
</view>
</view>
<view class="tips" wx:if="{{requireObj.violationAddress}}">{{requireObj.violationAddress}}</view>
</view>
<view class="info-item">
<view class="flex-between">
<view class="left flex-align">
<view class="require">*</view>
<view>违章行为</view>
</view>
<view class="right flex-align selectItem" data-fieldName="violationActShow" catch:tap="pickerChange">
<view wx:if="{{params.violationActText}}">{{params.violationActText}}</view>
<view class="placeholder" wx:else>请选择</view>
<view class="arrow"></view>
</view>
</view>
<view class="tips" wx:if="{{requireObj.violationActText}}">{{requireObj.violationActText}}</view>
</view>
<van-popup show="{{violationActShow}}" position="bottom" bind:close="hidePicker">
<van-picker data-fieldvalue="violationAct" default-index="{{violationActDefaultIdx}}" data-fieldname="violationActText" show-toolbar columns="{{violationBehavior}}" bind:confirm="pickerConfirm" bind:cancel="hidePicker">
</van-picker>
</van-popup>
<view class="info-item">
<view class="flex-between">
<view class="left flex-align">
<view>违章司机</view>
</view>
<view class="right">
<input placeholder-style="color:#949CB5" data-fieldName="violationDriver" bindinput="inputChange" value="{{params.violationDriver}}" type="text" placeholder="请输入" style="width:100%;" />
</view>
</view>
<view class="tips" wx:if="{{validate.violationDriver}}">{{validate.violationDriver}}</view>
</view>
<view class="info-item">
<view class="flex-between">
<view class="left flex-align">
<view>违章计分</view>
</view>
<view class="right">
<input type="number" placeholder-style="color:#949CB5" data-fieldName="violationScore" bindinput="inputChange" value="{{params.violationScore}}" placeholder="请输入" style="width:100%;" />
</view>
</view>
<view class="tips" wx:if="{{validate.violationScore}}">{{validate.violationScore}}</view>
</view>
<view class="info-item">
<view class="flex-between">
<view class="left flex-align">
<view>违章费用(元)</view>
</view>
<view class="right">
<input type="digit" placeholder-style="color:#949CB5" data-fieldName="violationCost" bindinput="inputChange" value="{{params.violationCost}}" placeholder="请输入" style="width:100%;" />
</view>
</view>
<view class="tips" wx:if="{{validate.violationCost}}">{{validate.violationCost}}</view>
</view>
<view class="info-item">
<view class="flex-between">
<view class="left flex-align">
<view>处理期限(天)</view>
</view>
<view class="right">
<input type="number" placeholder-style="color:#949CB5" data-fieldName="handleTerm" bindinput="inputChange" value="{{params.handleTerm}}" placeholder="请输入" style="width:100%;" />
</view>
</view>
<view class="tips" wx:if="{{validate.handleTerm}}">{{validate.handleTerm}}</view>
</view>
<view class="info-item borderBottomNone">
<view class="flex-between">
<view class="left flex-align">
<view>违章行为描述</view>
</view>
<view class="right">
<input type="text" placeholder-style="color:#949CB5" data-fieldName="violationActDes" bindinput="inputChange" value="{{params.violationActDes}}" type="text" placeholder="请输入" style="width:100%;" />
</view>
</view>
<view class="tips" wx:if="{{validate.violationActDes}}">{{validate.violationActDes}}</view>
</view>
</view>
<view class="operateBtn">
<view catchtap="formSubmit" class="submitBtn">提交</view>
</view>
<van-dialog id="van-dialog" />
</view>