添加司机

This commit is contained in:
cxc
2023-01-11 17:28:16 +08:00
parent 898b2d67d0
commit 0c1b9461ba
20 changed files with 2714 additions and 29 deletions

47
src/api/car/group.js Normal file
View File

@ -0,0 +1,47 @@
import request from '@/utils/request'
// 查询车辆分组列表
export function listGroup(query) {
return request({
url: '/car/group/list',
method: 'get',
params: query
})
}
// 查询车辆分组详细
export function getGroup(groupId,uuid) {
return request({
url: '/car/group/' + groupId + '/' + uuid,
method: 'get'
})
}
// 新增车辆分组
export function addGroup(data) {
return request({
url: '/car/group',
method: 'post',
data: data
})
}
// 修改车辆分组
export function updateGroup(data) {
return request({
url: '/car/group',
method: 'put',
data: data
})
}
// 删除车辆分组
export function delGroup(groupId) {
return request({
url: '/car/group',
method: 'delete',
data: {
ids: groupId
}
})
}

47
src/api/car/info.js Normal file
View File

@ -0,0 +1,47 @@
import request from '@/utils/request'
// 查询车辆信息列表
export function listInfo(query) {
return request({
url: '/car/info/list',
method: 'get',
params: query
})
}
// 查询车辆信息详细
export function getInfo(carId,uuid) {
return request({
url: '/car/info/' + carId + '/' + uuid,
method: 'get'
})
}
// 新增车辆信息
export function addInfo(data) {
return request({
url: '/car/info',
method: 'post',
data: data
})
}
// 修改车辆信息
export function updateInfo(data) {
return request({
url: '/car/info',
method: 'put',
data: data
})
}
// 删除车辆信息
export function delInfo(carId) {
return request({
url: '/car/info',
method: 'delete',
data: {
ids: carId
}
})
}

47
src/api/driver/info.js Normal file
View File

@ -0,0 +1,47 @@
import request from '@/utils/request'
// 查询司机信息列表
export function listInfo(query) {
return request({
url: '/driver/info/list',
method: 'get',
params: query
})
}
// 查询司机信息详细
export function getInfo(driverId,uuid) {
return request({
url: '/driver/info/' + driverId + '/' + uuid,
method: 'get'
})
}
// 新增司机信息
export function addInfo(data) {
return request({
url: '/driver/info',
method: 'post',
data: data
})
}
// 修改司机信息
export function updateInfo(data) {
return request({
url: '/driver/info',
method: 'put',
data: data
})
}
// 删除司机信息
export function delInfo(driverId) {
return request({
url: '/driver/info',
method: 'delete',
data: {
ids: driverId
}
})
}

25
src/api/region.js Normal file
View File

@ -0,0 +1,25 @@
import request from "@/utils/request";
// 获取某市所有区
export const listArea = (cityCode) =>
request({
url: "/system/region/allArea",
params: {
cityCode,
},
});
// 获取某省所有市
export const listCity = (provinceCode) =>
request({
url: "/system/region/allCity",
params: {
provinceCode,
},
});
// 获取所有省
export const listProvince = () =>
request({
url: "/system/region/allProvince",
});

BIN
src/assets/images/end.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/assets/images/start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,186 @@
<template>
<el-form
ref="formRef"
:model="modelValue"
:rules="rules"
:label-width="`${labelWidth}px`"
>
<el-row>
<el-col :span="24">
<el-form-item label="所在地:">
<el-row type="flex" justify="space-between">
<el-col :span="7">
<el-form-item prop="province">
<el-select
v-model="modelValue.province"
clearable
filterable
placeholder="请选择"
:disabled="provinceSelectList.length === 0"
@change="provinceChanged"
>
<el-option
v-for="item in provinceSelectList"
:key="item.provinceCode"
:label="item.provinceName"
:value="item.provinceCode"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item prop="city">
<el-select
v-model="modelValue.city"
clearable
filterable
:disabled="citySelectList.length === 0"
placeholder="请选择"
@change="cityChanged"
>
<el-option
v-for="item in citySelectList"
:key="item.cityCode"
:label="item.cityName"
:value="item.cityCode"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item prop="area">
<el-select
v-model="modelValue.area"
clearable
filterable
:disabled="districtSelectList.length === 0"
placeholder="请选择"
>
<el-option
v-for="item in districtSelectList"
:key="item.areaCode"
:label="item.areaName"
:value="item.areaCode"
>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script setup name="CityOptions">
import { listArea, listCity, listProvince } from "@/api/region";
import { reactive, ref, toRefs, watch } from "vue";
const props = defineProps({
modelValue: Object,
labelWidth: {
type: Number,
default: 120,
},
});
const { modelValue, labelWidth } = toRefs(props);
const formRef = ref(null);
const provinceSelectList = ref([]); // 省
const citySelectList = ref([]); // 市
const districtSelectList = ref([]); // 区\
const data = reactive({
rules: {
province: [
{ required: true, message: "请选择省", trigger: ["change", "blur"] },
],
city: [
{ required: true, message: "请选择市", trigger: ["change", "blur"] },
],
district: [
{ required: true, message: "请选择区", trigger: ["change", "blur"] },
],
},
});
const { rules } = toRefs(data);
// 获取 省列表
const getProvinceList = async () => {
const resp = await listProvince();
provinceSelectList.value = resp.data.map((el) => {
return { ...el, provinceCode: el.provinceCode.toString() };
});
};
// 获取市列表
const getCityListByProvinceId = async (provinceId) => {
const { data } = await listCity(provinceId);
citySelectList.value = data.map((el) => {
return {
...el,
cityCode: el.cityCode.toString(),
};
});
};
// 根据市id获取县区列表
const getAreaListByCityId = async (cityId) => {
const { data } = await listArea(cityId);
districtSelectList.value = data.map((el) => {
return {
...el,
areaCode: el.areaCode.toString(),
};
});
};
// 当省改变时
const provinceChanged = () => {
// 清除市县代码列表
modelValue.value.city = undefined;
modelValue.value.area = undefined;
// 清除市县列表
citySelectList.value = [];
districtSelectList.value = [];
// 重新请求城市列表
};
// 当市改变时
const cityChanged = () => {
// 清除县区代码列表
modelValue.value.area = undefined;
districtSelectList.value = [];
};
const validateForm = async () => {
try {
return await formRef.value.validate();
} catch (error) {
return false;
}
};
watch(
() => modelValue.value.province,
(val) => {
console.log("changed province");
val && getCityListByProvinceId(val);
},
{
immediate: true,
}
);
watch(
() => modelValue.value.city,
(val) => {
console.log("changed city");
val && getAreaListByCityId(val);
},
{
immediate: true,
}
);
getProvinceList();
defineExpose({
validateForm,
});
</script>

View File

@ -68,7 +68,7 @@ const props = defineProps({
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
default: true,
},
});
@ -79,33 +79,39 @@ const uploadList = ref([]);
const dialogImageUrl = ref("");
const dialogVisible = ref(false);
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
const uploadImgUrl = ref(
import.meta.env.VITE_APP_BASE_API + "/business/sysFile/upload"
); // 上传的图片服务器地址
const headers = ref({ Authorization: "Bearer " + getToken() });
const fileList = ref([]);
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize)
);
watch(() => props.modelValue, val => {
if (val) {
// 首先将值转为数组
const list = Array.isArray(val) ? val : props.modelValue.split(",");
// 然后将数组转为对象数组
fileList.value = list.map(item => {
if (typeof item === "string") {
if (item.indexOf(baseUrl) === -1) {
item = { name: baseUrl + item, url: baseUrl + item };
} else {
item = { name: item, url: item };
watch(
() => props.modelValue,
(val) => {
if (val) {
// 首先将值转为数组
const list = Array.isArray(val) ? val : props.modelValue.split(",");
// 然后将数组转为对象数组
fileList.value = list.map((item) => {
if (typeof item === "string") {
if (item.indexOf(baseUrl) === -1) {
item = { name: baseUrl + item, url: baseUrl + item };
} else {
item = { name: item, url: item };
}
}
}
return item;
});
} else {
fileList.value = [];
return [];
}
},{ deep: true, immediate: true });
return item;
});
} else {
fileList.value = [];
return [];
}
},
{ deep: true, immediate: true }
);
// 上传前loading加载
function handleBeforeUpload(file) {
@ -115,7 +121,7 @@ function handleBeforeUpload(file) {
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
isImg = props.fileType.some(type => {
isImg = props.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
@ -148,7 +154,7 @@ function handleExceed() {
// 上传成功回调
function handleUploadSuccess(res, file) {
if (res.code === 200) {
uploadList.value.push({ name: res.fileName, url: res.fileName });
uploadList.value.push({ name: res.fileName, url: res.url });
uploadedSuccessfully();
} else {
number.value--;
@ -161,7 +167,7 @@ function handleUploadSuccess(res, file) {
// 删除图片
function handleDelete(file) {
const findex = fileList.value.map(f => f.name).indexOf(file.name);
const findex = fileList.value.map((f) => f.name).indexOf(file.name);
if (findex > -1 && uploadList.value.length === number.value) {
fileList.value.splice(findex, 1);
emit("update:modelValue", listToString(fileList.value));
@ -172,7 +178,9 @@ function handleDelete(file) {
// 上传结束处理
function uploadedSuccessfully() {
if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
fileList.value = fileList.value
.filter((f) => f.url !== undefined)
.concat(uploadList.value);
uploadList.value = [];
number.value = 0;
emit("update:modelValue", listToString(fileList.value));
@ -208,6 +216,6 @@ function listToString(list, separator) {
<style scoped lang="scss">
// .el-upload--picture-card 控制加号部分
:deep(.hide .el-upload--picture-card) {
display: none;
display: none;
}
</style>
</style>

View File

@ -0,0 +1,320 @@
<template>
<div class="app-container">
<el-form
:model="queryParams"
ref="queryRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="分组名称" prop="groupName">
<el-input
v-model="queryParams.groupName"
placeholder="请输入分组名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="编码" prop="groupCode">
<el-input
v-model="queryParams.groupCode"
placeholder="请输入编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="租户ID" prop="tenantId">
<el-input
v-model="queryParams.tenantId"
placeholder="请输入租户ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> -->
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"
>搜索</el-button
>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['car:group:add']"
>新增</el-button
>
</el-col>
<!-- <el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['car:group:edit']"
>修改</el-button
>
</el-col> -->
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['car:group:remove']"
>删除</el-button
>
</el-col>
<!-- <el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['car:group:export']"
>导出</el-button
>
</el-col> -->
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="groupList"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="车辆分组ID" align="center" prop="groupId" />
<el-table-column label="分组名称" align="center" prop="groupName" />
<el-table-column label="编码" align="center" prop="groupCode" />
<!-- <el-table-column label="租户ID" align="center" prop="tenantId" /> -->
<el-table-column
label="操作"
align="center"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['car:group:edit']"
>修改</el-button
>
<el-button
link
type="primary"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['car:group:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改车辆分组对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="groupRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="分组名称" prop="groupName">
<el-input v-model="form.groupName" placeholder="请输入分组名称" />
</el-form-item>
<el-form-item label="编码" prop="groupCode">
<el-input v-model="form.groupCode" placeholder="请输入编码" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<!-- <el-form-item label="租户ID" prop="tenantId">
<el-input v-model="form.tenantId" placeholder="请输入租户ID" />
</el-form-item> -->
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Group">
import {
listGroup,
getGroup,
delGroup,
addGroup,
updateGroup,
} from "@/api/car/group";
const { proxy } = getCurrentInstance();
const groupList = ref([]);
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const title = ref("");
const data = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
groupName: null,
groupCode: null,
// tenantId: null,
},
rules: {},
});
const { queryParams, form, rules } = toRefs(data);
/** 查询车辆分组列表 */
function getList() {
loading.value = true;
listGroup(queryParams.value).then((response) => {
groupList.value = response.rows;
total.value = response.total;
loading.value = false;
});
}
// 取消按钮
function cancel() {
open.value = false;
reset();
}
// 表单重置
function reset() {
form.value = {
groupId: null,
groupName: null,
groupCode: null,
remark: null,
createTime: null,
createBy: null,
uuid: null,
// tenantId: null,
};
proxy.resetForm("groupRef");
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
handleQuery();
}
// 多选框选中数据
function handleSelectionChange(selection) {
ids.value = selection.map((item) => {
return { id: item.groupId, uuid: item.uuid };
});
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
function handleAdd() {
reset();
open.value = true;
title.value = "添加车辆分组";
}
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
const _groupId = row.groupId || ids.value;
getGroup(_groupId, row.uuid).then((response) => {
form.value = response.data;
open.value = true;
title.value = "修改车辆分组";
});
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["groupRef"].validate((valid) => {
if (valid) {
if (form.value.groupId != null) {
updateGroup(form.value).then((response) => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
} else {
addGroup(form.value).then((response) => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
});
}
}
});
}
/** 删除按钮操作 */
function handleDelete(row) {
const _groupIds = row.groupId
? [{ id: row.groupId, uuid: row.uuid }]
: ids.value;
proxy.$modal
.confirm(
'是否确认删除车辆分组编号为"' +
_groupIds.map((el) => el.groupId).join(",") +
'"的数据项?'
)
.then(function () {
return delGroup(_groupIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
"car/group/export",
{
...queryParams.value,
},
`group_${new Date().getTime()}.xlsx`
);
}
getList();
</script>

View File

@ -0,0 +1,291 @@
<template>
<div class="app-container">
<div class="title">添加车辆</div>
<el-form ref="infoRef" :model="form" :rules="rules" label-width="240px">
<el-form-item label="车牌号" prop="carCode">
<el-input v-model="form.carCode" placeholder="请输入车牌号" />
</el-form-item>
<el-form-item label="车辆名称" prop="carName">
<el-input v-model="form.carName" placeholder="请输入车辆名称" />
</el-form-item>
<el-form-item label="车辆容量" prop="carVolume">
<el-input v-model="form.carVolume" placeholder="请输入车辆容量" />
</el-form-item>
<el-form-item label="车辆型号" prop="carModel">
<el-input v-model="form.carModel" placeholder="请输入车辆型号" />
</el-form-item>
<el-form-item label="车架号" prop="frameCode">
<el-input v-model="form.frameCode" placeholder="请输入车架号" />
</el-form-item>
<el-form-item label="发动机号" prop="engineCode">
<el-input v-model="form.engineCode" placeholder="请输入发动机号" />
</el-form-item>
<el-form-item label="主要图片(一张)">
<image-upload v-model="form.mainPic" :limit="1" />
</el-form-item>
<el-form-item label="车辆类别" prop="carCategory">
<el-checkbox-group v-model="form.carCategory">
<el-checkbox label="1">行政公车</el-checkbox>
<el-checkbox label="2">物流货运</el-checkbox>
<el-checkbox label="3">旅游客运</el-checkbox>
<el-checkbox label="4">作业车辆</el-checkbox>
<el-checkbox label="5">通勤班车</el-checkbox>
<el-checkbox label="6">个人车辆</el-checkbox>
<el-checkbox label="7">其他车辆</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="行驶证" prop="vehicleLicense">
<image-upload v-model="form.vehicleLicense" :limit="1" />
<!-- <el-input v-model="form.vehicleLicense" placeholder="请输入行驶证" /> -->
</el-form-item>
<div class="form-item-group-title">类型信息</div>
<el-form-item label="分组" prop="groupId">
<el-select v-model="form.groupId" placeholder="请选择分组">
<ElOption
v-for="group in groupList"
:key="group.groupId"
:label="group.groupName"
:value="group.groupId"
/>
</el-select>
</el-form-item>
<el-form-item label="类型(1自有/2外来)" prop="type">
<el-radio-group v-model="form.type" class="ml-4">
<el-radio label="1">自有</el-radio>
<el-radio label="2">外来</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="form.status" placeholder="请选择状态">
<ElOption label="正常" value="0" />
<ElOption label="暂停" value="1" />
</el-select>
</el-form-item>
<div class="form-item-group-title">默认司机信息</div>
<el-form-item label="司机ID" prop="driverId">
<el-select v-model="form.driverId" placeholder="请选择司机ID">
<ElOption
v-for="driver in driverList"
:key="driver.driverId"
:label="driver.name"
:value="driver.driverId"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.driverId" label="司机手机号">
<el-input
:model-value="
driverList.find((el) => el.driverId === form.driverId)?.phone
"
></el-input>
</el-form-item>
<div class="form-item-group-title">车辆保养信息</div>
<el-form-item label="总里程数(km)" prop="totalMileage">
<el-input
v-model="form.totalMileage"
placeholder="请输入总里程数(km)"
/>
</el-form-item>
<el-form-item label="加油类型" prop="oilType">
<el-input
v-model="form.oilType"
placeholder="请输入内容如92#"
></el-input>
</el-form-item>
<el-form-item label="购置日期" prop="buyDate">
<el-input v-model="form.buyDate" placeholder="请输入购置日期" />
</el-form-item>
<div class="form-item-group-title">定位设备信息</div>
<el-form-item label="定位设备编码" prop="locateEquipmentCode">
<el-input
v-model="form.locateEquipmentCode"
placeholder="请输入定位设备编码"
/>
</el-form-item>
<el-form-item label="最大限速(km/h)" prop="maxSpeed">
<el-input v-model="form.maxSpeed" placeholder="请输入最大限速(km/h)" />
</el-form-item>
<el-form-item label="最大连续驾驶时间(分钟)" prop="maxContinuityTime">
<el-input
v-model="form.maxContinuityTime"
placeholder="请输入最大连续驾驶时间(分钟)"
/>
</el-form-item>
<el-form-item label="最大静止时间(分钟)" prop="maxStopTime">
<el-input
v-model="form.maxStopTime"
placeholder="请输入最大静止时间(分钟)"
/>
</el-form-item>
<div class="form-item-group-title">其他信息</div>
<el-form-item label="其他照片(<=6张)" prop="otherPhotos">
<!-- <el-input
v-model="form.otherPhotos"
type="textarea"
placeholder="请输入内容"
:limit="6"
/> -->
<image-upload v-model="form.otherPhotos" :limit="6" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="租户ID" prop="tenantId">
<el-input v-model="form.tenantId" placeholder="请输入租户ID" />
</el-form-item>
</el-form>
<el-row justify="center" :gutter="10">
<el-col :span="1.5">
<el-button type="primary" @click="submitForm">保存</el-button>
</el-col>
<el-col :span="1.5">
<el-button @click="back">取消</el-button>
</el-col>
</el-row>
</div>
</template>
<script setup name="AddCar">
import tab from "@/plugins/tab";
import { useRoute } from "vue-router";
import { listGroup } from "@/api/car/group";
import { reactive, toRefs, onMounted } from "vue";
import { getInfo, addInfo, updateInfo } from "@/api/car/info";
import { listInfo as listDriver } from "@/api/driver/info";
const route = useRoute();
const { proxy } = getCurrentInstance();
const groupList = ref([]); // 分组列表
const driverList = ref([]); // 司机列表
const data = reactive({
form: {
carCategory: [],
},
rules: {
carCode: [
{
required: true,
message: "请填写车牌号",
trigger: "blur",
},
],
carCategory: [
{
required: true,
message: "请选择车辆类型",
trigger: "change",
},
],
type: [
{
required: true,
message: "请选择类型(自有/外来)",
trigger: "change",
},
],
status: [
{
required: true,
message: "请选择状态",
trigger: "change",
},
],
},
});
const { form, rules } = toRefs(data);
// 表单重置
function reset() {
form.value = {
carId: null,
carCode: null,
carName: null,
carVolume: null,
carModel: null,
frameCode: null,
engineCode: null,
mainPic: null,
carCategory: [],
vehicleLicense: null,
groupId: null,
type: null,
status: "0",
driverId: null,
totalMileage: null,
oilType: null,
buyDate: null,
locateEquipmentCode: null,
maxSpeed: null,
maxContinuityTime: null,
maxStopTime: null,
otherPhotos: null,
remark: null,
createTime: null,
createBy: null,
uuid: null,
tenantId: null,
};
proxy.resetForm("infoRef");
}
function back() {
reset();
tab.closeOpenPage({ path: "/basic/car/info" });
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["infoRef"].validate((valid) => {
if (valid) {
// form.value.carCategory = form.value.carCategory.join(",");
if (form.value.carId != null) {
updateInfo({
...form.value,
carCategory: form.value.carCategory.join(","),
}).then((response) => {
proxy.$modal.msgSuccess("修改成功");
back();
});
} else {
addInfo({
...form.value,
carCategory: form.value.carCategory.join(","),
}).then((response) => {
proxy.$modal.msgSuccess("新增成功");
back();
});
}
}
});
}
onMounted(async () => {
reset();
if (route.query.carId && route.query.uuid) {
const resp = await getInfo(route.query.carId, route.query.uuid);
form.value = {
...resp.data,
carCategory: resp.data.carCategory
? resp.data.carCategory.split(",")
: [],
};
}
});
// 获取分组列表
listGroup().then((resp) => {
groupList.value = resp.rows;
});
listDriver().then((resp) => {
driverList.value = resp.rows;
});
</script>
<style scoped lang="scss">
.title {
font-size: 14px;
font-weight: 700;
padding: 10px 0;
}
.form-item-group-title {
width: 150px;
text-align: center;
margin: 10px 0 10px 26px;
border-bottom: 1px solid #bbb;
font-size: 14px;
color: #101010;
}
</style>

View File

@ -0,0 +1,476 @@
<template>
<div class="app-container">
<el-form
:model="queryParams"
ref="queryRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="车牌号" prop="carCode">
<el-input
v-model="queryParams.carCode"
placeholder="请输入车牌号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="车辆名称" prop="carName">
<el-input
v-model="queryParams.carName"
placeholder="请输入车辆名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="车辆容量" prop="carVolume">
<el-input
v-model="queryParams.carVolume"
placeholder="请输入车辆容量"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="车辆型号" prop="carModel">
<el-input
v-model="queryParams.carModel"
placeholder="请输入车辆型号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="车架号" prop="frameCode">
<el-input
v-model="queryParams.frameCode"
placeholder="请输入车架号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="发动机号" prop="engineCode">
<el-input
v-model="queryParams.engineCode"
placeholder="请输入发动机号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="车辆类别" prop="carCategory">
<el-input
v-model="queryParams.carCategory"
placeholder="请输入车辆类别"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="行驶证" prop="vehicleLicense">
<el-input
v-model="queryParams.vehicleLicense"
placeholder="请输入行驶证"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="分组ID" prop="groupId">
<el-input
v-model="queryParams.groupId"
placeholder="请输入分组ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="司机ID" prop="driverId">
<el-input
v-model="queryParams.driverId"
placeholder="请输入司机ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="总里程数(km)" prop="totalMileage">
<el-input
v-model="queryParams.totalMileage"
placeholder="请输入总里程数(km)"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="购置日期" prop="buyDate">
<el-input
v-model="queryParams.buyDate"
placeholder="请输入购置日期"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="定位设备编码" prop="locateEquipmentCode">
<el-input
v-model="queryParams.locateEquipmentCode"
placeholder="请输入定位设备编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最大限速(km/h)" prop="maxSpeed">
<el-input
v-model="queryParams.maxSpeed"
placeholder="请输入最大限速(km/h)"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最大连续驾驶时间(分钟)" prop="maxContinuityTime">
<el-input
v-model="queryParams.maxContinuityTime"
placeholder="请输入最大连续驾驶时间(分钟)"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="最大静止时间(分钟)" prop="maxStopTime">
<el-input
v-model="queryParams.maxStopTime"
placeholder="请输入最大静止时间(分钟)"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="租户ID" prop="tenantId">
<el-input
v-model="queryParams.tenantId"
placeholder="请输入租户ID"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> -->
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"
>搜索</el-button
>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['car:info:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['car:info:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['car:info:export']"
>导出</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain @click="goGroupManage"
>分组管理</el-button
>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="infoList"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="车辆ID" align="center" prop="carId" />
<el-table-column label="车牌号" align="center" prop="carCode" />
<el-table-column label="车辆名称" align="center" prop="carName" />
<!-- <el-table-column label="车辆容量" align="center" prop="carVolume" />
<el-table-column label="车辆型号" align="center" prop="carModel" />
<el-table-column label="车架号" align="center" prop="frameCode" />
<el-table-column label="发动机号" align="center" prop="engineCode" /> -->
<!-- <el-table-column
label="主要图片(一张)"
align="center"
prop="mainPic"
width="100"
>
<template #default="scope">
<image-preview :src="scope.row.mainPic" :width="50" :height="50" />
</template>
</el-table-column> -->
<!-- <el-table-column label="车辆类别" align="center" prop="carCategory" />
<el-table-column label="行驶证" align="center" prop="vehicleLicense" />
<el-table-column label="分组ID" align="center" prop="groupId" /> -->
<!-- <el-table-column label="类型(1自有/2外来)" align="center" prop="${field}">
<template #default="{ row }">
{{ "未知" }}
</template>
</el-table-column> -->
<!-- <el-table-column label="状态(0正常/1禁用)" align="center" prop="status" />
<el-table-column label="司机ID" align="center" prop="driverId" /> -->
<!-- <el-table-column
label="总里程数(km)"
align="center"
prop="totalMileage"
/> -->
<!-- <el-table-column label="加油类型" align="center" prop="${field}">
<template #default="{ row }">
{{ "未知" }}
</template>
</el-table-column>
<el-table-column label="购置日期" align="center" prop="buyDate" /> -->
<el-table-column
label="定位设备编码"
align="center"
prop="locateEquipmentCode"
/>
<!-- <el-table-column label="最大限速(km/h)" align="center" prop="maxSpeed" />
<el-table-column
label="最大连续驾驶时间(分钟)"
align="center"
prop="maxContinuityTime"
/>
<el-table-column
label="最大静止时间(分钟)"
align="center"
prop="maxStopTime"
/>
<el-table-column
label="其他照片(<=6张)"
align="center"
prop="otherPhotos"
/> -->
<el-table-column label="备注" align="center" prop="remark" />
<!-- <el-table-column label="租户ID" align="center" prop="tenantId" /> -->
<el-table-column
label="操作"
align="center"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['car:info:edit']"
>修改</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script setup name="Info">
import {
listInfo,
// getInfo,
delInfo,
// addInfo,
// updateInfo,
} from "@/api/car/info";
import { useRouter } from "vue-router";
const router = useRouter();
const { proxy } = getCurrentInstance();
const infoList = ref([]);
// const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
// const title = ref("");
const data = reactive({
// form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
carCode: null,
carName: null,
carVolume: null,
carModel: null,
frameCode: null,
engineCode: null,
mainPic: null,
carCategory: null,
vehicleLicense: null,
groupId: null,
type: null,
status: null,
driverId: null,
totalMileage: null,
oilType: null,
buyDate: null,
locateEquipmentCode: null,
maxSpeed: null,
maxContinuityTime: null,
maxStopTime: null,
otherPhotos: null,
tenantId: null,
},
// rules: {},
});
const { queryParams } = toRefs(data);
/** 查询车辆信息列表 */
function getList() {
loading.value = true;
listInfo(queryParams.value).then((response) => {
infoList.value = response.rows;
total.value = response.total;
loading.value = false;
});
}
// // 取消按钮
// function cancel() {
// open.value = false;
// reset();
// }
// 表单重置
// function reset() {
// form.value = {
// carId: null,
// carCode: null,
// carName: null,
// carVolume: null,
// carModel: null,
// frameCode: null,
// engineCode: null,
// mainPic: null,
// carCategory: null,
// vehicleLicense: null,
// groupId: null,
// type: null,
// status: "0",
// driverId: null,
// totalMileage: null,
// oilType: null,
// buyDate: null,
// locateEquipmentCode: null,
// maxSpeed: null,
// maxContinuityTime: null,
// maxStopTime: null,
// otherPhotos: null,
// remark: null,
// createTime: null,
// createBy: null,
// uuid: null,
// tenantId: null,
// };
// proxy.resetForm("infoRef");
// }
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
handleQuery();
}
// 多选框选中数据
function handleSelectionChange(selection) {
ids.value = selection.map((item) => {
return { id: item.carId, uuid: item.uuid };
});
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
function handleAdd() {
router.push({
path: "/basic/car/info/add",
});
}
/** 修改按钮操作 */
function handleUpdate(row) {
// reset();
const _carId = row.carId || ids.value;
router.push({
path: "/basic/car/info/add",
query: {
carId: _carId,
uuid: row.uuid,
},
});
}
/** 删除按钮操作 */
function handleDelete(row) {
const _carIds = row.carId ? [{ id: row.carId, uuid: row.uuid }] : ids.value;
proxy.$modal
.confirm(
'是否确认删除车辆信息编号为"' +
_carIds.map((el) => el.carId).join(",") +
'"的数据项?'
)
.then(function () {
return delInfo(_carIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
"car/info/export",
{
...queryParams.value,
},
`info_${new Date().getTime()}.xlsx`
);
}
function goGroupManage() {
router.push({
path: "/basic/car/group",
});
}
getList();
</script>

View File

@ -0,0 +1,236 @@
<template>
<div class="app-container">
<div class="title">添加司机</div>
<el-form ref="infoRef" :model="form" :rules="rules" label-width="240px">
<el-form-item label="司机姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入司机姓名" />
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="form.gender" class="ml-4">
<el-radio label="0" size="large">男</el-radio>
<el-radio label="1" size="large">女</el-radio>
<el-radio label="2" size="large">未知</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input
v-model="form.phone"
placeholder="请输入手机号"
:maxlength="11"
/>
</el-form-item>
<el-form-item label="身份证" prop="idCard">
<el-input v-model="form.idCard" placeholder="请输入身份证" />
</el-form-item>
<el-form-item label="员工编号" prop="employeeNo">
<el-input v-model="form.employeeNo" placeholder="请输入员工编号" />
</el-form-item>
<CitySelect v-model="form" :label-width="240"></CitySelect>
<!-- <el-form-item label="省" prop="province">
<el-input v-model="form.province" placeholder="请输入省" />
</el-form-item>
<el-form-item label="市" prop="city">
<el-input v-model="form.city" placeholder="请输入市" />
</el-form-item>
<el-form-item label="区" prop="area">
<el-input v-model="form.area" placeholder="请输入区" />
</el-form-item> -->
<el-form-item label="详细地址" prop="address">
<el-input v-model="form.address" placeholder="请输入详细地址" />
</el-form-item>
<div class="form-item-group-title">类型信息</div>
<el-form-item label="部门" prop="deptId">
<el-tree-select
v-model="form.deptId"
:data="deptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
placeholder="请选择归属部门"
check-strictly
:render-after-expand="false"
/>
</el-form-item>
<el-form-item label="司机类型" prop="driverType">
<el-radio-group v-model="form.driverType" class="ml-4">
<el-radio label="0" size="large">员工</el-radio>
<el-radio label="1" size="large">车队司机</el-radio>
<el-radio label="2" size="large">外来司机</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status" class="ml-4">
<el-radio label="0" size="large">正常</el-radio>
<el-radio label="1" size="large">休息</el-radio>
<el-radio label="2" size="large">停用</el-radio>
</el-radio-group>
</el-form-item>
<div class="form-item-group-title">车辆信息</div>
<el-form-item label="车辆" prop="carId">
<el-select v-model="form.carId" placeholder="请选择车辆">
<ElOption
v-for="car in carList"
:key="car.carId"
:label="car.carName"
:value="car.carId"
/>
</el-select>
</el-form-item>
<div class="form-item-group-title">其他信息</div>
<el-form-item label="驾驶证" prop="driverLicense">
<image-upload v-model="form.driverLicense" />
</el-form-item>
<el-form-item label="其他照片" prop="otherPhotos">
<image-upload v-model="form.otherPhotos" />
</el-form-item>
<el-form-item label="紧急联系人姓名" prop="urgentUser">
<el-input
v-model="form.urgentUser"
placeholder="请输入紧急联系人姓名"
/>
</el-form-item>
<el-form-item label="紧急联系人电话" prop="urgentPhone">
<el-input
v-model="form.urgentPhone"
placeholder="请输入紧急联系人电话"
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<!-- <el-form-item label="租户ID" prop="tenantId">
<el-input v-model="form.tenantId" placeholder="请输入租户ID" />
</el-form-item>
<el-form-item label="用户ID" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户ID" />
</el-form-item> -->
</el-form>
<el-row justify="center" :gutter="10">
<el-col :span="1.5">
<el-button type="primary" @click="submitForm">保存</el-button>
</el-col>
<el-col :span="1.5">
<el-button @click="back">取消</el-button>
</el-col>
</el-row>
</div>
</template>
<script setup name="AddDriver">
import { reactive, toRefs, ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { getInfo, updateInfo, addInfo } from "@/api/driver/info";
import { listInfo as listCarInfo } from "@/api/car/info";
import { deptTreeSelect } from "@/api/system/user";
import CitySelect from "@/components/CitySelect";
import tab from "@/plugins/tab";
const route = useRoute();
const { proxy } = getCurrentInstance();
const deptOptions = ref(undefined);
const carList = ref([]);
const data = reactive({
form: {},
rules: {
name: [{ required: true, message: "请输入司机姓名", trigger: "blur" }],
idCard: [{ required: true, message: "请输入身份证号", trigger: "blur" }],
status: [{ required: true, message: "请选择状态", trigger: "change" }],
phone: [
{ required: true, message: "请输入司机手机号", trigger: "blur" },
{
pattern: /^1[3456789]\d{9}$/,
message: "手机号格式不正确",
trigger: "blur",
},
],
deptId: [
{
required: true,
message: "请选择部门",
trigger: "change",
},
],
driverType: [
{
required: true,
message: "请选择司机类型",
trigger: "change",
},
],
},
});
const { form, rules } = toRefs(data);
// 表单重置
function reset() {
form.value = {
driverId: null,
name: null,
gender: null,
phone: null,
idCard: null,
employeeNo: null,
province: null,
city: null,
area: null,
address: null,
deptId: null,
driverType: null,
status: "0",
carId: null,
driverLicense: null,
otherPhotos: null,
urgentUser: null,
urgentPhone: null,
remark: null,
uuid: null,
tenantId: null,
userId: null,
};
proxy.resetForm("infoRef");
}
/** 提交按钮 */
function submitForm() {
proxy.$refs["infoRef"].validate((valid) => {
if (valid) {
if (form.value.driverId != null) {
updateInfo(form.value).then((response) => {
proxy.$modal.msgSuccess("修改成功");
back();
});
} else {
addInfo(form.value).then((response) => {
proxy.$modal.msgSuccess("新增成功");
back();
});
}
}
});
}
function back() {
reset();
tab.closeOpenPage({ path: "/basic/driver/info" });
}
onMounted(async () => {
reset();
if (route.query.driverId && route.query.uuid) {
const { data } = await getInfo(route.query.driverId, route.query.uuid);
form.value = data;
}
});
listCarInfo().then((resp) => {
carList.value = resp.rows;
});
/** 查询部门下拉树结构 */
deptTreeSelect().then((response) => {
deptOptions.value = response.data;
});
</script>
<style lang="scss" scoped>
.form-item-group-title {
width: 150px;
text-align: center;
margin: 10px 0 10px 26px;
border-bottom: 1px solid #bbb;
font-size: 14px;
color: #101010;
}
</style>

View File

@ -0,0 +1,217 @@
<template>
<div class="app-container">
<el-form
:model="queryParams"
ref="queryRef"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="司机姓名" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入司机姓名"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input
v-model="queryParams.phone"
placeholder="请输入手机号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"
>搜索</el-button
>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['driver:info:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['driver:info:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['driver:info:export']"
>导出</el-button
>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="infoList"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="司机ID" align="center" prop="driverId" />
<el-table-column label="司机姓名" align="center" prop="name" />
<el-table-column label="手机号" align="center" prop="phone" />
<el-table-column
label="操作"
align="center"
class-name="small-padding fixed-width"
>
<template #default="scope">
<el-button
link
type="primary"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['driver:info:edit']"
>修改</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script setup name="Info">
import { listInfo, delInfo } from "@/api/driver/info";
import { useRouter } from "vue-router";
const { proxy } = getCurrentInstance();
const router = useRouter();
const infoList = ref([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
name: null,
phone: null,
},
});
const { queryParams } = toRefs(data);
/** 查询司机信息列表 */
function getList() {
loading.value = true;
listInfo(queryParams.value).then((response) => {
infoList.value = response.rows;
total.value = response.total;
loading.value = false;
});
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef");
handleQuery();
}
// 多选框选中数据
function handleSelectionChange(selection) {
ids.value = selection.map((item) => {
return { id: item.driverId, uuid: item.uuid };
});
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
function handleAdd() {
router.push({
path: "/basic/driver/info/add",
});
}
/** 修改按钮操作 */
function handleUpdate(row) {
const _driverId = row.driverId || ids.value;
router.push({
path: "/basic/driver/info/add",
query: {
driverId: _driverId,
uuid: row.uuid,
},
});
}
/** 删除按钮操作 */
function handleDelete(row) {
const _driverIds = row.driverId
? [{ id: row.driverId, uuid: row.uuid }]
: ids.value;
proxy.$modal
.confirm(
'是否确认删除司机信息编号为"' +
_driverIds.map((el) => el.driverId).join(",") +
'"的数据项?'
)
.then(function () {
return delInfo(_driverIds);
})
.then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
})
.catch(() => {});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
"driver/info/export",
{
...queryParams.value,
},
`info_${new Date().getTime()}.xlsx`
);
}
getList();
</script>

View File

@ -0,0 +1,160 @@
<template>
<div class="description">
<div class="roads">
<div class="road-item" v-for="(road, index) in roadsList" :key="road">
{{ road }}
<ElIcon v-if="index != roadsList.length - 1">
<ArrowRight />
</ElIcon>
</div>
</div>
<div class="time-distance">
<span class="time">{{ (routeExtData.time / 60).toFixed(1) }}分钟</span>
(<span class="distance">{{ routeExtData.distance }}</span>)
<span class="seg">|</span>
<span class="policy">
{{ routeExtData.policy }}
</span>
</div>
</div>
<div
@click="emit('start-click')"
class="steps-item start"
v-if="stepsList.length"
>
<div
class="icon"
:style="{
backgroundPosition: stepIconMap.get('起点'),
}"
></div>
<div class="content">起点</div>
</div>
<div
v-for="(item, index) in stepsList"
:key="item.start_location.lng"
class="steps-item"
@click="handleRouteClick(item, index)"
>
<div
class="icon"
:style="{
backgroundPosition: stepIconMap.get(item.action) ?? '-46px -23px',
}"
></div>
<div class="content">
{{ item.instruction }}
</div>
</div>
<div
@click="emit('end-click')"
class="steps-item start"
v-if="stepsList.length"
>
<div
class="icon"
:style="{
backgroundPosition: stepIconMap.get('终点'),
}"
></div>
<div class="content">终点</div>
</div>
</template>
<script setup name="StepsList">
import { ArrowRight } from "@element-plus/icons-vue";
import { computed, toRefs } from "vue";
const emit = defineEmits(["step-click", "start-click", "end-click"]);
const props = defineProps({
stepsList: {
type: Array,
required: true,
},
routeExtData: {
type: Object,
required: true,
},
});
const { stepsList } = toRefs(props);
const roadsList = computed(() =>
stepsList.value
? [...new Set(stepsList.value.map((el) => el.road).filter((el) => !!el))]
: []
);
const stepIconMap = new Map([
["靠左", "-406px -23px"],
["靠右", "-445px -23px"],
["左转", "-87px -23px"],
["右转", "-124px -23px"],
["左转调头", "-327px -23px"],
["向左前方行驶", "-165px -23px"],
["向右前方行驶", "-206px -23px"],
["向左后方行驶", "-246px -23px"],
["减速行驶", "-524px -23px"],
["终点", "-126px -104px"],
["起点", "-47px -104px"],
]);
const handleRouteClick = (data, index) => {
emit("step-click", data, index);
};
</script>
<style scoped lang="scss">
.description {
border-bottom: 1px solid #e6e6e6;
padding: 8px 8px;
.roads {
display: flex;
align-items: center;
font-size: 13px;
font-weight: bold;
overflow: hidden;
.road-item {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
display: flex;
align-items: center;
flex-shrink: 0;
}
}
.time-distance {
display: flex;
margin-top: 5px;
font-size: 13px;
.seg {
margin: 0px 3px;
}
}
}
.steps-item {
display: flex;
align-items: center;
cursor: pointer;
padding-bottom: 5px;
padding-top: 5px;
&:not(:last-child) {
// margin-bottom: 10px;
border-bottom: 1px solid #f0f0f0;
}
.icon {
margin-right: 8px;
width: 20px;
height: 20px;
color: #fff;
line-height: 20px;
border-radius: 50%;
border: 1px solid #ddd;
background-color: #fff;
// background-position: -47px -104px;
background-image: url(https://webapi.amap.com/theme/v1.3/images/newpc/diricon.png);
}
.content {
font-size: 12px;
}
&.start .content {
font-size: 14px;
font-weight: 700;
}
}
</style>

View File

@ -0,0 +1,625 @@
<template>
<div id="map-container"></div>
<el-drawer
v-model="drawer"
direction="ltr"
:close-on-click-modal="false"
modal-class="control-drawer-modal"
size="25%"
:modal="false"
:show-close="false"
:with-header="false"
>
<el-row justify="center">
<el-radio-group v-model="carType" size="small">
<el-radio-button label="car">小车</el-radio-button>
<el-radio-button label="truck">货车</el-radio-button>
</el-radio-group>
</el-row>
<el-row :gutter="10" justify="space-between">
<el-col :span="2">
<div
:style="{
height: '100%',
display: 'flex',
alignItems: 'center',
}"
>
<el-icon color="#30a1f0"><Sort /></el-icon>
</div>
</el-col>
<el-col :span="20">
<el-form ref="routeFormRef" id="route-form" :model="formData">
<el-form-item prop="startName">
<el-popover
:width="300"
:visible="showStartSuggestion && !!formData.startName"
title="搜索建议"
>
<div
v-for="item in searchTips"
:key="item.id"
class="suggestion-item"
@click="searchPoint(item.name, 'start')"
>
{{ item.name }}
</div>
<template #reference>
<el-input
v-model="formData.startName"
size="small"
@keydown.enter="searchPoint(formData.startName, 'start')"
@input="handleInput('start', $event)"
@focus="
nowSearch = 'start';
showStartSuggestion = true;
"
@blur="
showStartSuggestion = false;
nowSearch = '';
"
:suffix-icon="
formData.startLngLat.length ? `circle-check-filled` : null
"
>
<template #prepend>起</template>
</el-input>
</template>
</el-popover>
</el-form-item>
<el-form-item prop="endName">
<el-popover
:width="300"
:visible="showEndSuggestion && !!formData.endName"
title="搜索建议"
>
<div
v-for="item in searchTips"
:key="item.id"
class="suggestion-item"
@click="searchPoint(item.name, 'end')"
>
{{ item.name }}
</div>
<template #reference>
<el-input
v-model="formData.endName"
size="small"
@input="getAutoComplete"
@keydown.enter="searchPoint(formData.endName, 'end')"
@focus="
nowSearch = 'end';
// map.clearMap();
showEndSuggestion = true;
"
@blur="
showEndSuggestion = false;
nowSearch = '';
// searchTips = [];
"
:suffix-icon="
formData.endLngLat.length ? `circle-check-filled` : null
"
>
<template #prepend>终</template>
</el-input>
</template>
</el-popover>
</el-form-item>
</el-form>
</el-col>
<el-col :span="2">
<div
:style="{
height: '100%',
display: 'flex',
alignItems: 'center',
}"
>
<el-icon color="#30a1f0"><CirclePlusFilled /></el-icon>
</div>
</el-col>
</el-row>
<el-row justify="space-between">
<el-button link @click="clearRoute" size="small">清除路线</el-button>
<el-button type="primary" @click="searchRoute" size="small"
>查询</el-button
>
</el-row>
<el-tabs
v-if="showStepPane"
v-model="activeTabName"
@tab-click="handleTabClick"
>
<!-- :disabled="!stepsList.length" -->
<el-tab-pane label="费用最低" name="LEAST_FEE">
<div id="panel"></div>
</el-tab-pane>
<!-- :disabled="!stepsList.length" -->
<el-tab-pane label="时间最短" name="LEAST_TIME">
<route-step-list
:steps-list="stepsList"
:route-ext-data="routeExtData"
@start-click="map.setFitView([startMarker])"
@end-click="map.setFitView([endMarker])"
@step-click="handleRouteClick"
/></el-tab-pane>
<!-- :disabled="!stepsList.length" -->
<el-tab-pane label="距离最短" name="LEAST_DISTANCE">
<route-step-list
:steps-list="stepsList"
:route-ext-data="routeExtData"
@step-click="handleRouteClick"
@start-click="map.setFitView([startMarker])"
@end-click="map.setFitView([endMarker])"
/>
</el-tab-pane>
</el-tabs>
</el-drawer>
<el-button
id="drawer-switch"
type="primary"
circle
:icon="drawer ? ArrowLeftBold : ArrowRightBold"
@click="switchDrawer"
></el-button>
</template>
<script setup>
import AMapLoader from "@amap/amap-jsapi-loader";
import startPng from "@/assets/images/start.png";
import endPng from "@/assets/images/end.png";
import { ArrowLeftBold, ArrowRightBold } from "@element-plus/icons-vue";
import markBsPng from "@/assets/images/mark_bs.png";
import { ElMessage } from "element-plus";
import { computed, onMounted, reactive, ref, shallowRef, toRefs } from "vue";
import RouteStepList from "./RouteStepList.vue";
const map = shallowRef(null);
const AMap = shallowRef(null);
const carType = ref("car"); // car: 小车, truck: 卡车
const driving = shallowRef(null); // 驾车路线规划插件
const truckDriving = shallowRef(null); // 卡车路线规划插件
const geocoder = shallowRef(null); // 经纬度转地址插件
const placeSearch = shallowRef(null); // 地点搜索插件
const autoComplete = shallowRef(null); // 搜索建议插件
const startMarker = shallowRef(null); // 起点标记
const endMarker = shallowRef(null); // 终点标记
const resultInfoWindow = shallowRef(null);
const searchResultMarkers = ref([]); // 搜索结果表笔列表
const stepsPolylines = ref([]); // 路线步骤标记列表
const stepsList = ref([]);
const showStepPane = computed(
() =>
formData.value.startLngLat.length === 2 &&
formData.value.endLngLat.length === 2
);
const routePlugins = {
car: driving,
truck: truckDriving,
};
const drawer = ref(true);
const routeFormRef = ref(null);
const searchTips = ref([]);
const nowSearch = ref("");
const showStartSuggestion = ref(false);
const showEndSuggestion = ref(false);
const activeTabName = ref("LEAST_FEE");
const data = reactive({
formData: {
startLngLat: [],
startName: "",
endName: "",
endLngLat: [],
},
routeExtData: {
distance: 0,
time: 0,
policy: "",
},
});
const { formData, routeExtData } = toRefs(data);
// 初始化地图
const initMap = async () => {
try {
AMap.value = await AMapLoader.load({
key: "377d7c36dd385e2a722f29d4c6e1ffbf", // 申请好的Web端开发者Key首次调用 load 时必填
// key: "7891f1238368a895ff1967c79643102d", // 申请好的Web端开发者Key首次调用 load 时必填
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: [
"AMap.Driving",
"AMap.Scale",
"AMap.PlaceSearch",
"AMap.AutoComplete",
"AMap.Geocoder",
"AMap.TruckDriving",
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
AMapUI: {
// 是否加载 AMapUI缺省不加载
version: "1.1", // AMapUI 版本
plugins: ["overlay/SimpleMarker"], // 需要加载的 AMapUI ui插件
},
Loca: {
// 是否加载 Loca 缺省不加载
version: "2.0", // Loca 版本
},
});
map.value = new AMap.value.Map("map-container", {
zoom: 16, // 缩放级别
center: [117.290345, 31.797813], // 中心点
city: "合肥",
});
window.mapins = map.value;
/** 加载插件 */
// 驾车路线规划插件
driving.value = new AMap.value.Driving({
// 驾车路线规划策略AMap.DrivingPolicy.LEAST_TIME是最快捷模式
// policy: AMap.DrivingPolicy.MULTI_POLICIES,
// map: map.value,
panel: "panel",
});
// 卡车路线规划插件 TODO:
truckDriving.value = new AMap.value.TruckDriving({ map: map.value });
// 地点搜索插件
placeSearch.value = new AMap.value.PlaceSearch({
// map: map.value,
city: "0551",
});
geocoder.value = new AMap.value.Geocoder();
// 搜索建议插件
autoComplete.value = new AMap.value.AutoComplete({});
/** 加载插件结束 */
resultInfoWindow.value = new AMap.value.InfoWindow({
anchor: "bottom-center",
});
startMarker.value = new AMap.value.Marker({
icon: startPng,
anchor: "bottom-center",
});
endMarker.value = new AMap.value.Marker({
icon: endPng,
anchor: "bottom-center",
});
// city: "010", //城市设为北京,默认:“全国”
// radius: 1000, //范围默认500
// AMap.value.Event.addListener(placeSearch.value, "markerClick", (e) => {
// //添加事件
// console.log(e); //获取点标注位置
// if (nowSearch.value === "start") {
// formData.value.startName = e.data.name;
// formData.value.startLngLat = [e.data.location.lng, e.data.location.lat];
// } else if (nowSearch.value === "end") {
// formData.value.endName = e.data.name;
// formData.value.endLngLat = [e.data.location.lng, e.data.location.lat];
// }
// });
map.value.on("click", handleMapClick);
map.value.addControl(new AMap.value.Scale());
} catch (error) {
console.log(error);
}
};
const handleInput = (type, data) => {
getAutoComplete(data);
};
// 地图点击
const handleMapClick = (ev) => {
// 起点输入框 focus 时
if (nowSearch.value === "start") {
map.value.clearMap();
if (formData.value.endLngLat.length === 2) {
map.value.add(endMarker.value);
}
startMarker.value.setPosition(
new AMap.value.LngLat(ev.lnglat.lng, ev.lnglat.lat)
);
map.value.add(startMarker.value);
regeoCode([ev.lnglat.lng, ev.lnglat.lat])
.then((address) => {
formData.value.startName = address;
})
.catch(() => {
formData.value.startName = `${ev.lnglat.lng}, ${ev.lnglat.lat}`;
});
formData.value.startLngLat = [ev.lnglat.lng, ev.lnglat.lat];
}
// 终点输入框 focus 时
else if (nowSearch.value === "end") {
map.value.clearMap();
if (formData.value.startLngLat.length === 2) {
map.value.add(startMarker.value);
}
endMarker.value.setPosition(
new AMap.value.LngLat(ev.lnglat.lng, ev.lnglat.lat)
);
map.value.add(endMarker.value);
regeoCode([ev.lnglat.lng, ev.lnglat.lat])
.then((address) => {
formData.value.endName = address;
})
.catch(() => {
formData.value.endName = `${ev.lnglat.lng}, ${ev.lnglat.lat}`;
});
formData.value.endLngLat = [ev.lnglat.lng, ev.lnglat.lat];
}
};
const handleRouteClick = (data, index) => {
console.log(data, index);
map.value.setFitView([stepsPolylines.value[index]]);
};
const routeFixView = (idx) => {
console.log(idx);
map.value.setFitView(stepsPolylines.value[idx]);
};
// 根据经纬度获取地址
const regeoCode = (lnglat /** [lng, lat] */) => {
return new Promise((resolve, reject) => {
geocoder.value.getAddress(lnglat, (status, result) => {
if (status === "complete" && result.regeocode) {
const address = result.regeocode.formattedAddress;
resolve(address);
} else {
reject("根据经纬度查询地址失败");
}
});
});
};
// tab点击事件
const handleTabClick = (tab, ev) => {
activeTabName.value = tab.paneName;
if (formData.value.startLngLat.length && formData.value.endLngLat.length) {
searchRoute();
} else {
ElMessage.error("err");
}
};
// 清除路线reset form
const clearRoute = () => {
if (routeFormRef.value) {
routeFormRef.value.resetFields();
}
formData.value.startLngLat = [];
formData.value.endLngLat = [];
stepsList.value = [];
routeExtData.value = {};
map.value.clearMap();
};
// 搜索路线
const searchRoute = () => {
if (!driving.value) return;
return new Promise((resolve, reject) => {
map.value.clearMap();
driving.value.opt.policy = AMap.value.DrivingPolicy[activeTabName.value];
const startLngLat = formData.value.startLngLat;
const endLngLat = formData.value.endLngLat;
driving.value.search(startLngLat, endLngLat, (status, result) => {
// 未出错时result即是对应的路线规划方案
if (status === "complete") {
stepsList.value = result.routes[0].steps;
routeExtData.value.distance = result.routes[0].distance;
routeExtData.value.time = result.routes[0].time;
routeExtData.value.policy = result.routes[0].policy;
console.log(result);
drawRoute(result);
resolve(result);
} else {
reject("获取路线失败");
}
});
});
};
// 绘制路线
const drawRoute = (result) => {
const startLngLat = formData.value.startLngLat;
const endLngLat = formData.value.endLngLat;
map.value.add(startMarker.value);
new AMap.value.Polyline({
map: map.value,
strokeWeight: 8,
strokeOpacity: 1,
strokeColor: "#808080",
strokeStyle: "dashed",
path: [
new AMap.value.LngLat(...startLngLat),
new AMap.value.LngLat(
result.routes[0].steps[0].start_location.lng,
result.routes[0].steps[0].start_location.lat
),
],
});
new AMap.value.Polyline({
map: map.value,
strokeWeight: 8,
strokeOpacity: 1,
strokeColor: "#808080",
strokeStyle: "dashed",
path: [
new AMap.value.LngLat(
result.routes[0].steps[
result.routes[0].steps.length - 1
].end_location.lng,
result.routes[0].steps[
result.routes[0].steps.length - 1
].end_location.lat
),
new AMap.value.LngLat(...endLngLat),
],
});
stepsPolylines.value = result.routes[0].steps.map((el) => {
return new AMap.value.Polyline({
map: map.value,
strokeWeight: 8,
strokeOpacity: 1,
strokeColor: "#1bac2e",
showDir: true,
path: el.path.map((item) => new AMap.value.LngLat(item.lng, item.lat)),
});
});
map.value.add(endMarker.value);
map.value.setFitView(stepsPolylines.value);
};
// 切换左侧面板显示隐藏
const switchDrawer = () => {
drawer.value = !drawer.value;
if (drawer.value) {
document.querySelector("#drawer-switch").style.left = "25%";
document.querySelector("#drawer-switch").style.transform =
"translate(-50%, -50%)";
} else {
document.querySelector("#drawer-switch").style.left = "0";
document.querySelector("#drawer-switch").style.transform =
"translate(0, -50%)";
}
};
// 根据名称搜索地图标点
const searchPoint = (val /** 搜索建议选项 */, type) => {
if (val) {
if (nowSearch.value === "start") {
formData.value.startName = val;
} else if (nowSearch.value === "end") {
formData.value.endName = val;
} else {
return;
}
} else {
return;
}
map.value.clearMap();
if (type === "start" && formData.value.endLngLat.length === 2) {
map.value.add(endMarker.value);
} else if (type === "end" && formData.value.startLngLat.length === 2) {
map.value.add(startMarker.value);
}
placeSearch.value.search(val, (status, result) => {
console.log(status, result);
// TODO: 添加搜索结果标记
if (status === "complete") {
searchResultMarkers.value = result.poiList.pois.map((el, index) => {
const marker = new AMap.value.Marker({
map: map.value,
position: new AMap.value.LngLat(el.location.lng, el.location.lat),
extData: el,
content: `
<div
style="
background-image: url(${markBsPng});
width: 19px;
height: 31px;
background-size: cover;
color: #fff;
text-align: center;
font-size: 12px;
padding-top: 3px;
"
>
${index + 1}
</div>
`,
});
marker.on("click", handleResultClick);
return marker;
});
map.value.setFitView(searchResultMarkers.value);
}
});
};
const handleResultClick = (ev) => {
if (nowSearch.value === "start") {
formData.value.startName = ev.target.getExtData().name;
formData.value.startLngLat = [ev.lnglat.lng, ev.lnglat.lat];
} else if (nowSearch.value === "end") {
formData.value.endName = ev.target.getExtData().name;
formData.value.endLngLat = [ev.lnglat.lng, ev.lnglat.lat];
}
resultInfoWindow.value.setContent(`
<div>
${ev.target.getExtData().name}</div>
`);
resultInfoWindow.value.open(map.value, [ev.lnglat.lng, ev.lnglat.lat]);
};
// 获取搜索建议
const getAutoComplete = (val) => {
console.log("changed");
if (!autoComplete.value) return;
autoComplete.value.search(val, (status, result) => {
console.log(status, result);
if (status === "complete") {
searchTips.value = result.tips;
}
});
};
const setCenter = (val) => {
if (!val) return;
placeSearch.value.search(val, (status, result) => {
console.log(status, result);
});
};
onMounted(() => {
initMap();
});
</script>
<style scoped lang="scss">
#map-container {
width: 100%;
// height: calc(100vh-84px);
}
#route-form {
margin-top: 20px;
}
#drawer-switch {
position: absolute;
top: 50%;
left: 25%;
z-index: 99999;
transform: translate(-50%, -50%);
}
.suggestion-item {
user-select: none;
cursor: pointer;
padding: 0 32px 0 20px;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
color: #606266;
height: 34px;
line-height: 34px;
box-sizing: border-box;
text-overflow: ellipsis;
&:hover {
background-color: #f5f7fa;
}
}
</style>
<style lang="scss">
.control-drawer-modal {
pointer-events: none;
.el-drawer {
pointer-events: initial;
.el-input__suffix {
color: #67c23a;
}
}
}
</style>