记录一个基本的车辆信息管理页面,由豆包撰写完成,只需要微调页面即可。
主要功能是车辆信息的查询、新增、编辑,项目用到了uniapp、vue3、ts、uni-ui、z-paging
页面效果如下:
以上界面均由豆包生成,完成度非常高,增、删、查、改都实现了,加上组件的能力如z-paging、uni-ui组件库,列表的分页查询、弹出窗等为用户带来良好的交互体验。
以下是前端源码(90%豆包+10%人工)
<template><z-paging ref="zPageRef" @query="fetchVehicleList" v-model="dataList"><!-- 搜索和筛选 --><template #top><!-- 头部导航 --><view class="header"><view class="search-bar"><uni-search-bar v-model="searchQuery" placeholder="搜索车牌号码、车主姓名或品牌"@confirm="handleSearch"></uni-search-bar></view><button class="add-btn" @click="showAddModal"><uni-icons type="plusempty" size="24" color="#0f80ff"></uni-icons><text>新增车辆</text></button></view></template><!-- 车辆列表 --><view class="vehicle-management"><view class="vehicle-list"><view class="vehicle-items"><view class="vehicle-item" v-for="(vehicle, index) in dataList" :key="index"><view class="vehicle-info"><view class="plate-number"><text>{{ vehicle.carNum }}</text><uni-tag :type="vehicle.type=='商户' ? 'primary' : 'success'":text="vehicle.type=='商户' ? '商户' : '内部员工'"></uni-tag></view><view class="basic-info"><text class="name">车主: {{ vehicle.name }}</text><text class="tel">电话: {{ vehicle.tel }}</text></view><view class="basic-info"><text class="tel">职务: {{ vehicle.duty }}</text></view><view class="basic-info"><text class="tel">商户名称: {{ vehicle.brandName }}</text><text class="tel">展位号: {{ vehicle.boothNum }}</text></view></view><view class="vehicle-actions"><button class="view-btn" @click="viewVehicle(vehicle)"><uni-icons type="info" size="20" color="#0f80ff"></uni-icons></button><button class="edit-btn" @click="editVehicle(vehicle)"><uni-icons type="compose" size="20" color="#0f80ff"></uni-icons></button><button class="delete-btn" @click="confirmDelete(vehicle)"><uni-icons type="trash" size="20" color="#ff4500"></uni-icons></button></view></view></view></view><!-- 车辆表单模态框 --><uni-popup ref="vehicleFormPopup" type="center" :custom-style="popupStyle"><view class="vehicle-form"><view class="form-header"><text class="form-title">{{ isEditing ? '编辑车辆信息' : '新增车辆信息' }}</text><uni-icons type="closeempty" size="24" color="#666" @click="closeFormModal"></uni-icons></view><view class="form-content"><!-- 基本信息 --><view class="form-section"><view class="form-item"><text class="item-label">车牌号码</text><view class="plate-input"><picker :range="provinces" @change="onProvinceChange"><view class="province-selector"><text>{{ formData.province }}</text><uni-icons type="arrowdown" size="18" color="#999"></uni-icons></view></picker><input v-model="formData.carNum" placeholder="请输入车牌号码" maxlength="6" /></view></view><view class="form-item"><text class="item-label">车主姓名</text><input v-model="formData.name" placeholder="请输入车主姓名" /></view><view class="form-item"><text class="item-label">联系电话</text><input v-model="formData.tel" placeholder="请输入联系电话" type="number" /></view><view class="form-item"><text class="item-label">车辆类型</text><picker :range="types" @change="onTypeChange"><view class="type-selector"><text>{{ formData.type || '请选择车辆类型' }}</text><uni-icons type="arrowdown" size="18" color="#999"></uni-icons></view></picker></view></view><!-- 商户 --><view v-if="formData.type=='商户'" class="form-section"><view class="form-item"><text class="item-label">商户名称/品牌:</text><input v-model="formData.brandName" placeholder="商户名称/品牌" /></view><view class="form-item"><text class="item-label">展位号</text><input v-model="formData.boothNum" placeholder="请输入展位号" /></view></view><!-- 内部员工 --><view v-if="formData.type=='内部员工'" class="form-section"><view class="form-item"><text class="item-label">职务</text><input v-model="formData.duty" placeholder="请输入职务" /></view></view><view class="form-actions"><button class="cancel-btn" @click="closeFormModal">取消</button><button class="save-btn" @click="saveVehicle">保存</button></view></view></view></uni-popup><!-- 车辆详情模态框 --><uni-popup ref="vehicleDetailPopup" type="bottom" :custom-style="popupStyle"><view class="vehicle-detail"><view class="detail-header"><text class="detail-title">车辆详情</text><uni-icons type="closeempty" size="24" color="#666" @click="closeDetailModal"></uni-icons></view><view class="detail-content"><view class="detail-info"><view class="plate-number"><text>{{ selectedVehicle.carNum }}</text><uni-tag :type="selectedVehicle.brandName ? 'primary' : 'success'":text="selectedVehicle.brandName ? '商户' : '内部员工'"></uni-tag></view><view class="info-group"><view class="info-item"><text class="info-label">车主姓名:</text><text class="info-value">{{ selectedVehicle.name }}</text></view><view class="info-item"><text class="info-label">联系电话:</text><text class="info-value">{{ selectedVehicle.tel }}</text></view><view v-if="selectedVehicle.brandName" class=""><view class="info-item"><text class="info-label">商户品牌:</text><text class="info-value">{{ selectedVehicle.brand }}</text></view><view class="info-item"><text class="info-label">展位号:</text><text class="info-value">{{ selectedVehicle.boothNum }}</text></view></view><view v-else class=""><view class="info-item"><text class="info-label">职务:</text><text class="info-value">{{ selectedVehicle.duty }}</text></view></view><view class="info-item"><text class="info-label">登记时间:</text><text class="info-value">{{ formatDate(selectedVehicle.inputtime*1000) }}</text></view><view class="info-item"><text class="info-label">违规次数:</text><text class="info-value">{{ selectedVehicle.violationNum || '无' }}</text></view></view></view><!-- 违规记录 --><view class="violation-records"v-if="selectedVehicle.violations && selectedVehicle.violations.length > 0"><text class="records-title">违规记录 ({{ selectedVehicle.violations.length }})</text><view class="record-item" v-for="(violation, index) in selectedVehicle.violations":key="index"><view class="record-header"><text class="record-time">{{ violation.time }}</text><textclass="record-type {{ violation.type === '占用多位' ? 'type-multiple' : 'type-forbidden' }}">{{ violation.type }}</text></view><text class="record-location">位置: {{ violation.location }}</text><text class="record-punishment">处理: {{ violation.punishment }}</text></view></view></view><view class="detail-actions"><button class="edit-btn" @click="editFromDetail">编辑信息</button><button class="violation-btn" @click="reportViolation">上报违规</button></view></view></uni-popup><!-- 自定义底部导航 --><CustomTabBar :tabList="tabList" :activeColor="'#409EFF'" :normalColor="'#666'" /></view></z-paging>
</template><script setup lang="ts">import { ref, reactive, onMounted, watch, computed } from 'vue';import { useRouter } from 'vue-router';import CustomTabBar from '@/components/CustomTabBar.vue'import request from '../../../utils/http2';import { formatDate } from '@/utils/date'// 页面数据const searchQuery = ref('');const selectedVehicleType = ref('');const selectedStatus = ref('');const zPageRef = ref();const vehicleFormPopup = ref(null);const vehicleDetailPopup = ref(null);const popupStyle = ref('height: 60vh; border-radius: 20rpx 20rpx 0 0;');const dataList = ref({})// 数据配置const provinces = ref(['京', '津', '冀', '晋', '蒙', '辽', '吉', '黑','沪', '苏', '浙', '皖', '闽', '赣', '鲁', '豫','鄂', '湘', '粤', '桂', '琼', '渝', '川', '贵','云', '藏', '陕', '甘', '青', '宁', '新']);const types = ['商户', '内部员工'];const statusOptions = ref(['全部', '正常', '违规', '黑名单']);// 底部导航配置const tabList = ref([{name: 'record',path: '/pages/ffep/car/car',text: '过夜登记',icon: 'checkbox',activeIcon: 'checkbox-filled'},{name: 'violation',path: 'parkingViolation',text: '违规登记',icon: 'close',activeIcon: 'clear'},{name: 'manager',path: 'manager',text: '车辆管理',icon: 'calendar',activeIcon: 'calendar-filled'},{name: 'statistics',path: '/pages/ffep/car/statistics',text: '统计',icon: 'info',activeIcon: 'info-filled'}])// 表单数据const formData = reactive({id: '',province: '粤',carNum: '',name: '',tel: '',duty: '',brandName: '',boothNum: '',status: '正常',remark: '',registerTime: '',type: ''});// 选中的车辆const selectedVehicle = reactive({id: '',carNum: '',name: '',tel: '',duty: '',brandName: '',boothNum: '',status: '',violations:[]});// 状态const isEditing = ref(false);const loadingText = ref('加载中...');const onTypeChange = (e) => {formData.type = types[e.detail.value]}// 加载车辆列表const fetchVehicleList = async (pageIndex : number, pageSize : number) => {try {// 构建查询参数const params = {pageNo: pageIndex,pageSize: pageSize,keyword: searchQuery.value,duty: selectedVehicleType.value || undefined,status: selectedStatus.value || undefined};// 调用API获取车辆列表const res = await request({url: '/api/ffep/parking/carlist',method: 'GET',data: params});if (res.code === 1) {zPageRef.value.complete(res.data)} else {uni.showToast({title: res.msg || '获取数据失败',icon: 'none'});zPageRef.value.complete(false);}} catch (error) {console.error('获取车辆列表失败', error);uni.showToast({title: '网络错误,请稍后重试',icon: 'none'});zPageRef.value.complete(false);}};// 刷新列表const onRefresh = () => {zPageRef.value?.refresh();};// 加载更多const onLoadMore = () => {zPageRef.value?.loadMore();};// 处理搜索const handleSearch = () => {onRefresh();};// 显示新增车辆模态框const showAddModal = () => {// 重置表单resetForm();isEditing.value = false;vehicleFormPopup.value.open();};// 查看车辆详情const viewVehicle = (vehicle : any) => {// 复制车辆数据到详情对象Object.assign(selectedVehicle, vehicle);// 获取车辆违规记录getVehicleViolations(vehicle.carNum);vehicleDetailPopup.value.open();};// 获取车辆违规记录const getVehicleViolations = async (carNum : string) => {const res = await request({url: `/api/ffep/parking/getVehicleViolations`,method: 'POST',data: {carNum: carNum}});if (res.code === 1) {selectedVehicle.violations = res.data || [];} else {selectedVehicle.violations = [];uni.showToast({title: res.msg || '获取违规记录失败',icon: 'none'});}};// 编辑车辆const editVehicle = (vehicle : any) => {// 复制车辆数据到表单Object.assign(formData, vehicle);isEditing.value = true;vehicleFormPopup.value.open();};// 从详情页编辑const editFromDetail = () => {// 复制详情数据到表单Object.assign(formData, selectedVehicle);isEditing.value = true;// 关闭详情模态框,打开表单模态框vehicleDetailPopup.value.close();setTimeout(() => {vehicleFormPopup.value.open();}, 300);};// 上报违规const reportViolation = () => {// 关闭详情模态框vehicleDetailPopup.value.close();// 跳转到违规上报页面,传递车辆IDuni.navigateTo({url: `/pages/violation/report?vehicleId=${selectedVehicle.id}`});};// 表单相关事件处理const onProvinceChange = (e : any) => {formData.province = provinces.value[e.detail.value];};// 重置表单const resetForm = () => {formData.id = '';formData.province = '粤';formData.carNum = '';formData.name = '';formData.tel = '';formData.duty = '';formData.brand = '';formData.color = '';formData.status = '正常';formData.remark = '';formData.registerTime = '';};// 关闭表单模态框const closeFormModal = () => {vehicleFormPopup.value.close();};// 关闭详情模态框const closeDetailModal = () => {vehicleDetailPopup.value.close();};// 保存车辆信息const saveVehicle = async () => {// 表单验证if (!formData.carNum.trim()) {uni.showToast({title: '请输入车牌号码',icon: 'none'});return;}if (!formData.name.trim()) {uni.showToast({title: '请输入车主姓名',icon: 'none'});return;}if (!formData.tel.trim()) {uni.showToast({title: '请输入联系电话',icon: 'none'});return;}if (!formData.type) {uni.showToast({title: '请选择车辆类型',icon: 'none'});return;}try {let apiUrl = '';let method = '';if (isEditing.value) {// 编辑apiUrl = `/api/ffep/parking/editcar`;method = 'PUT';} else {// 新增apiUrl = '/api/ffep/parking/addcar';method = 'POST';}// 调用API保存车辆信息const res = await request({url: apiUrl,method,data: formData});if (res.code === 1) {uni.showToast({title: res.msg,icon: 'success'});// 关闭模态框closeFormModal();// 刷新列表onRefresh();} else {uni.showToast({title: res.msg || (isEditing.value ? '更新失败' : '添加失败'),icon: 'none'});}} catch (error) {console.error('保存车辆信息失败', error);uni.showToast({title: '网络错误,请稍后重试',icon: 'none'});}};// 确认删除车辆const confirmDelete = (vehicle : any) => {uni.showModal({title: '确认删除',content: `确定要删除车牌为${vehicle.carNum}的车辆信息吗?`,success: async (res) => {if (res.confirm) {try {// 调用API删除车辆const result = await request({url: `/api/ffep/parking/delcar`,method: 'DELETE',data:{id:vehicle.id}});if (result.code === 1) {uni.showToast({title: '删除成功',icon: 'success'});// 刷新列表onRefresh();} else {uni.showToast({title: result.msg || '删除失败',icon: 'none'});}} catch (error) {console.error('删除车辆失败', error);uni.showToast({title: '网络错误,请稍后重试',icon: 'none'});}}}});};// 页面加载时初始化onMounted(() => {zPageRef.value.reload()// 初始化微信JSSDK(如果需要)// initWecomJssdk();});
</script><style lang="scss" scoped>.vehicle-management {min-height: 100vh;background-color: #f5f5f5;}.header {display: flex;align-items: center;justify-content: space-between;height: 100rpx;background-color: #0f80ff;padding: 0 30rpx;.add-btn {display: flex;align-items: center;background-color: #ffffff;color: #0f80ff;border-radius: 20rpx;padding: 8rpx 20rpx;height: 75%;text {margin-left: 10rpx;font-size: 28rpx;}}}.vehicle-list {padding: 0 20rpx;.vehicle-items {.vehicle-item {display: flex;justify-content: space-between;background-color: #fff;border-radius: 16rpx;padding: 20rpx;margin-bottom: 20rpx;box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);.vehicle-info {flex: 1;.plate-number {display: flex;font-size: 32rpx;font-weight: bold;color: #333;margin-bottom: 10rpx;}.basic-info {display: flex;margin-bottom: 10rpx;.name,.tel {font-size: 26rpx;color: #666;margin-right: 20rpx;}}.extra-info {display: flex;flex-wrap: wrap;.type,.brand,.status {font-size: 24rpx;color: #999;margin-right: 20rpx;margin-bottom: 10rpx;}.status-normal {color: #00b42a;}.status-warning {color: #ff7d00;}}}.vehicle-actions {display: flex;flex-direction: column;justify-content: space-around;button {width: 60rpx;height: 60rpx;display: flex;align-items: center;justify-content: center;background-color: #f5f5f5;border-radius: 50%;margin-bottom: 10rpx;}}}}}// 表单模态框样式.vehicle-form {background-color: #fff;border-radius: 20rpx 20rpx 0 0;height: 100%;overflow: hidden;.form-header {display: flex;align-items: center;justify-content: space-between;padding: 30rpx;border-bottom: 1rpx solid #e5e6eb;.form-title {font-size: 32rpx;font-weight: bold;color: #333;}}.form-content {padding: 20rpx 30rpx;height: calc(100% - 160rpx);overflow-y: auto;.form-section {margin-bottom: 30rpx;.section-title {font-size: 28rpx;font-weight: bold;color: #333;margin-bottom: 20rpx;}.form-item {margin-bottom: 25rpx;.item-label {display: block;font-size: 26rpx;color: #666;margin-bottom: 10rpx;}.plate-input {display: flex;.province-selector {width: 120rpx;height: 80rpx;display: flex;align-items: center;justify-content: center;border: 1rpx solid #e5e6eb;border-radius: 10rpx;margin-right: 20rpx;font-size: 32rpx;}input {flex: 1;border: 1rpx solid #e5e6eb;border-radius: 10rpx;padding: 0 20rpx;font-size: 32rpx;}}input,textarea {width: 100%;border: 1rpx solid #e5e6eb;border-radius: 10rpx;padding: 20rpx;font-size: 28rpx;}.type-selector,.status-selector {display: flex;align-items: center;justify-content: space-between;border: 1rpx solid #e5e6eb;border-radius: 10rpx;padding: 20rpx;font-size: 28rpx;}}}}.form-actions {display: flex;padding: 30rpx;border-top: 1rpx solid #e5e6eb;.cancel-btn,.save-btn {flex: 1;height: 90rpx;border-radius: 50rpx;font-size: 32rpx;display: flex;align-items: center;justify-content: center;}.cancel-btn {color: #666;border: 1rpx solid #e5e6eb;margin-right: 30rpx;}.save-btn {color: #fff;background-color: #0f80ff;}}}// 详情模态框样式.vehicle-detail {background-color: #fff;border-radius: 20rpx 20rpx 0 0;height: 100%;overflow: hidden;.detail-header {display: flex;align-items: center;justify-content: space-between;padding: 30rpx;border-bottom: 1rpx solid #e5e6eb;.detail-title {font-size: 32rpx;font-weight: bold;color: #333;width: 200rpx;}}.detail-content {padding: 20rpx 30rpx;height: calc(100% - 160rpx);overflow-y: auto;.detail-info {margin-bottom: 30rpx;.plate-number {display: flex;align-items: center;font-size: 36rpx;font-weight: bold;color: #333;margin-bottom: 20rpx;.status {font-size: 26rpx;font-weight: normal;margin-left: 20rpx;padding: 5rpx 15rpx;border-radius: 50rpx;}.status-normal {color: #00b42a;background-color: #e8fff3;}.status-warning {color: #ff7d00;background-color: #fff7e8;}}.info-group {.info-item {display: flex;align-items: center;margin-bottom: 20rpx;.info-label {width: 200rpx;font-size: 28rpx;color: #666;}.info-value {flex: 1;font-size: 28rpx;color: #333;}}}}.violation-records {.records-title {font-size: 28rpx;font-weight: bold;color: #333;margin-bottom: 20rpx;}.record-item {background-color: #f9f9f9;border-radius: 10rpx;padding: 20rpx;margin-bottom: 20rpx;.record-header {display: flex;align-items: center;justify-content: space-between;margin-bottom: 10rpx;.record-time {font-size: 26rpx;color: #666;}.record-type {font-size: 24rpx;padding: 5rpx 15rpx;border-radius: 50rpx;}.type-multiple {color: #ff7d00;background-color: #fff7e8;}.type-forbidden {color: #f53f3f;background-color: #ffeded;}}.record-location,.record-punishment {font-size: 26rpx;color: #666;margin-bottom: 5rpx;}}}}.detail-actions {display: flex;padding: 30rpx;border-top: 1rpx solid #e5e6eb;.edit-btn,.violation-btn {flex: 1;height: 90rpx;border-radius: 50rpx;font-size: 32rpx;display: flex;align-items: center;justify-content: center;}.edit-btn {color: #0f80ff;border: 1rpx solid #0f80ff;margin-right: 30rpx;}.violation-btn {color: #fff;background-color: #0f80ff;}}}
</style>
生成拿到的代码只需要根据业务需求,删改或调整部分字段,完成api接口数据对接,页面都可以直接投入到业务使用场景了,整个页面耗时(包括API接口后端开发)不到3小时完成,个人来说已经是非常高效,不得不感慨科技改变生活!!
当然不足也是有的,例如细节的地方需要微调,比如布局、样式,AI虽然强大但是会耗费非常多的沟通时间,这部分建议手动修改。其次是AI有时候未必了解组件的使用方式,例如z-paging组件标签的位置,经过多次沟通调整还是没办法正常运行,终归还是得靠自己把控吧!