截止‘运力管理’模块
This commit is contained in:
426
src/views/vehicle/accident-record.vue
Normal file
426
src/views/vehicle/accident-record.vue
Normal file
@@ -0,0 +1,426 @@
|
||||
<template>
|
||||
<basic-container class="accident-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('accident_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('accident_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('accident_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('accident_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="输入车牌号/船号查询选择"
|
||||
value-key="value"
|
||||
class="accident-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="事故记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/accident-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/accident-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('accident_record_add'),
|
||||
viewBtn: this.hasPermission('accident_record_view'),
|
||||
delBtn: this.hasPermission('accident_record_delete'),
|
||||
editBtn: this.hasPermission('accident_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'form.vehicleType': {
|
||||
handler(value) {
|
||||
this.updateVehicleTypeDisplays(value || '车辆');
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
updateVehicleTypeDisplays(vehicleType) {
|
||||
const vehicleTypeColumn = this.findColumn(this.option.column, 'vehicleType');
|
||||
vehicleTypeColumn.disabled = Boolean(this.form.id);
|
||||
if (!this.form.id && this.form.vehicleNo) {
|
||||
this.form.vehicleNo = '';
|
||||
}
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
const vehicleType = this.form.vehicleType || '车辆';
|
||||
const request = vehicleType === '船舶' ? getShipList : getVehicleList;
|
||||
const params =
|
||||
vehicleType === '船舶' ? { shipIdentifierNo: queryString } : { plateNo: queryString };
|
||||
request(1, 20, params)
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(
|
||||
records.map(item => ({
|
||||
value: vehicleType === '船舶' ? item.shipIdentifierNo || item.shipName : item.plateNo,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
values.vehicleType = values.vehicleType || '车辆';
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (Number(row.directEconomicLoss) < 0) {
|
||||
this.$message.warning('直接经济损失不能小于 0');
|
||||
return false;
|
||||
}
|
||||
if (Number(row.insuranceClaimAmount) < 0) {
|
||||
this.$message.warning('保险理赔金额不能小于 0');
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.directEconomicLoss !== undefined &&
|
||||
row.directEconomicLoss !== null &&
|
||||
row.directEconomicLoss !== '' &&
|
||||
row.insuranceClaimAmount !== undefined &&
|
||||
row.insuranceClaimAmount !== null &&
|
||||
row.insuranceClaimAmount !== '' &&
|
||||
Number(row.insuranceClaimAmount) > Number(row.directEconomicLoss)
|
||||
) {
|
||||
this.$message.warning('保险理赔金额不能超过直接经济损失金额');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form = {
|
||||
vehicleType: '车辆',
|
||||
};
|
||||
this.updateVehicleTypeDisplays('车辆');
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
this.updateVehicleTypeDisplays(detail.vehicleType);
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出事故记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/accident-record/export-accident-record', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `事故记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/accident-record/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '事故记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.accident-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
441
src/views/vehicle/annual-inspection-record.vue
Normal file
441
src/views/vehicle/annual-inspection-record.vue
Normal file
@@ -0,0 +1,441 @@
|
||||
<template>
|
||||
<basic-container class="annual-inspection-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('annual_inspection_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('annual_inspection_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('annual_inspection_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('annual_inspection_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="输入车牌号/船号查询选择"
|
||||
value-key="value"
|
||||
class="annual-inspection-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="年检记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/annual-inspection-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/annual-inspection-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('annual_inspection_record_add'),
|
||||
viewBtn: this.hasPermission('annual_inspection_record_view'),
|
||||
delBtn: this.hasPermission('annual_inspection_record_delete'),
|
||||
editBtn: this.hasPermission('annual_inspection_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'form.vehicleType': {
|
||||
handler(value) {
|
||||
this.updateVehicleTypeDisplays(value || '车辆');
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
updateVehicleTypeDisplays(vehicleType) {
|
||||
const vehicleTypeColumn = this.findColumn(this.option.column, 'vehicleType');
|
||||
const vehicleLevelColumn = this.findColumn(this.option.column, 'vehicleTechnicalLevel');
|
||||
const shipTypeColumn = this.findColumn(this.option.column, 'shipInspectionType');
|
||||
const passengerColumn = this.findColumn(this.option.column, 'passengerTypeLevel');
|
||||
vehicleTypeColumn.disabled = Boolean(this.form.id);
|
||||
vehicleLevelColumn.display = vehicleType !== '船舶';
|
||||
shipTypeColumn.display = vehicleType === '船舶';
|
||||
passengerColumn.display = vehicleType !== '船舶';
|
||||
if (vehicleType === '船舶') {
|
||||
this.form.vehicleTechnicalLevel = undefined;
|
||||
this.form.passengerTypeLevel = undefined;
|
||||
} else {
|
||||
this.form.shipInspectionType = undefined;
|
||||
}
|
||||
if (!this.form.id && this.form.vehicleNo) {
|
||||
this.form.vehicleNo = '';
|
||||
}
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
const vehicleType = this.form.vehicleType || '车辆';
|
||||
const request = vehicleType === '船舶' ? getShipList : getVehicleList;
|
||||
const params =
|
||||
vehicleType === '船舶' ? { shipIdentifierNo: queryString } : { plateNo: queryString };
|
||||
request(1, 20, params)
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(
|
||||
records.map(item => ({
|
||||
value: vehicleType === '船舶' ? item.shipIdentifierNo || item.shipName : item.plateNo,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
values.vehicleType = values.vehicleType || '车辆';
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
if (values.vehicleType === '船舶') {
|
||||
values.vehicleTechnicalLevel = undefined;
|
||||
values.passengerTypeLevel = undefined;
|
||||
} else {
|
||||
values.shipInspectionType = undefined;
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (Number(row.fee) < 0) {
|
||||
this.$message.warning('费用不能小于 0');
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.inspectionAssessmentDate &&
|
||||
row.validUntilDate &&
|
||||
new Date(row.validUntilDate.replace(/-/g, '/')).getTime() <=
|
||||
new Date(row.inspectionAssessmentDate.replace(/-/g, '/')).getTime()
|
||||
) {
|
||||
this.$message.warning('有效期截止日应大于检测评定日期');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form = {
|
||||
vehicleType: '车辆',
|
||||
inspectionAssessmentDate: this.$dayjs().format('YYYY-MM-DD'),
|
||||
};
|
||||
this.updateVehicleTypeDisplays('车辆');
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
this.updateVehicleTypeDisplays(detail.vehicleType);
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出年检记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob(
|
||||
'/blade-transport/annual-inspection-record/export-annual-inspection-record',
|
||||
this.buildExportParams()
|
||||
)
|
||||
.then(res => {
|
||||
downloadXls(res.data, `年检记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/annual-inspection-record/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '年检记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.annual-inspection-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
862
src/views/vehicle/credit-score-quantification.vue
Normal file
862
src/views/vehicle/credit-score-quantification.vue
Normal file
@@ -0,0 +1,862 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
:permission="permissionList"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
plain
|
||||
v-if="hasPermission('credit_score_quantification_add')"
|
||||
@click="openConfig()"
|
||||
>新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('credit_score_quantification_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('credit_score_quantification_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('credit_score_quantification_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('credit_score_quantification_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #name="{ row }">
|
||||
<el-button type="primary" link @click="openConfig(row, true)">
|
||||
{{ row.name }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-tag :type="statusMap[row.status].type">
|
||||
{{ statusMap[row.status].label }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-edit"
|
||||
v-if="hasPermission('credit_score_quantification_edit')"
|
||||
@click="openConfig(row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-position"
|
||||
v-if="hasPermission('credit_score_quantification_publish') && row.status !== 1"
|
||||
@click="handlePublish(row)"
|
||||
>
|
||||
发布
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
:icon="row.status === 1 ? 'el-icon-close' : 'el-icon-check'"
|
||||
v-if="hasPermission('credit_score_quantification_status') && row.status !== 3"
|
||||
@click="handleStatus(row)"
|
||||
>
|
||||
{{ row.status === 1 ? '停用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
text
|
||||
icon="el-icon-delete"
|
||||
v-if="hasPermission('credit_score_quantification_delete') && row.status === 3"
|
||||
@click="rowDel(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<el-dialog title="评分量化表数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
:title="readonly ? '查看评分量化表' : configForm.id ? '编辑评分量化表' : '新增评分量化表'"
|
||||
append-to-body
|
||||
v-model="configBox"
|
||||
width="88%"
|
||||
class="score-dialog"
|
||||
@closed="resetConfig"
|
||||
>
|
||||
<div class="score-config">
|
||||
<div class="section-title">评分量化表</div>
|
||||
<el-form :model="configForm" label-width="110px" class="base-form" :disabled="readonly">
|
||||
<el-form-item label="评定表名称" required>
|
||||
<el-input v-model="configForm.name" maxlength="30" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="configForm.remark" type="textarea" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="section-head">
|
||||
<div class="section-title">评分表</div>
|
||||
</div>
|
||||
<el-table :data="configForm.categories" border class="config-table">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" />
|
||||
<el-table-column label="评分类目" prop="categoryName" min-width="180" />
|
||||
<el-table-column label="评分项目数" width="140" align="center">
|
||||
<template #default="{ row }">{{ row.items.length }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" text @click="openCategory(row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="section-head standard-head">
|
||||
<div class="section-title">评估标准</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
v-if="!readonly"
|
||||
@click="openStandard()"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="configForm.standards" border class="config-table">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" />
|
||||
<el-table-column label="信用等级" prop="creditLevel" width="120" align="center" />
|
||||
<el-table-column label="得分率" width="160" align="center">
|
||||
<template #default="{ row }">{{ row.scoreRateLower }}% - {{ row.scoreRateUpper }}%</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最大资金使用额度(万元)" width="210" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.creditLimitLower }} - {{ row.creditLimitUpper }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标准说明" prop="standardDescription" min-width="320" />
|
||||
<el-table-column label="操作" width="160" align="center">
|
||||
<template #default="{ row, $index }">
|
||||
<el-button type="primary" text @click="openStandard(row, $index)">编辑</el-button>
|
||||
<el-button type="danger" text v-if="!readonly" @click="removeStandard($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="configBox = false">{{ readonly ? '关闭' : '取消' }}</el-button>
|
||||
<el-button type="primary" v-if="!readonly" @click="saveConfig">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="评分类目详情"
|
||||
append-to-body
|
||||
v-model="categoryBox"
|
||||
width="82%"
|
||||
class="score-dialog"
|
||||
>
|
||||
<div class="category-detail">
|
||||
<div class="category-line">评分类目:{{ currentCategory.categoryName }}</div>
|
||||
<div class="section-head">
|
||||
<span></span>
|
||||
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="openItem()">
|
||||
添加
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="currentCategory.items" border class="config-table">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" />
|
||||
<el-table-column label="评分项目" prop="itemName" width="180" />
|
||||
<el-table-column label="得分说明" prop="scoreDescription" min-width="260" />
|
||||
<el-table-column label="选项数" width="100" align="center">
|
||||
<template #default="{ row }">{{ row.options.length }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center">
|
||||
<template #default="{ row, $index }">
|
||||
<el-button type="primary" text @click="openItem(row, $index)">编辑</el-button>
|
||||
<el-button type="danger" text v-if="!readonly" @click="removeItem($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="categoryBox = false">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="评分项目详情" append-to-body v-model="itemBox" width="76%">
|
||||
<el-form :model="itemForm" label-width="100px" :disabled="readonly">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="评分项目" required>
|
||||
<el-input v-model="itemForm.itemName" maxlength="20" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="得分说明">
|
||||
<el-input v-model="itemForm.scoreDescription" maxlength="300" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="选项描述">
|
||||
<el-input v-model="itemForm.optionDescription" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="选项" required>
|
||||
<div class="option-editor">
|
||||
<div class="option-row" v-for="(option, index) in itemForm.options" :key="index">
|
||||
<el-input v-model="option.optionName" maxlength="100" placeholder="选项描述" />
|
||||
<el-input-number v-model="option.score" :min="0" :precision="2" placeholder="分值" />
|
||||
<span class="score-unit">分</span>
|
||||
<el-button type="danger" text v-if="!readonly && itemForm.options.length > 1" @click="removeOption(index)">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-button type="primary" text v-if="!readonly" @click="addOption">+添加选项</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="itemBox = false">取消</el-button>
|
||||
<el-button type="primary" v-if="!readonly" @click="saveItem">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="评估标准设置" append-to-body v-model="standardBox" width="76%">
|
||||
<el-form :model="standardForm" label-width="170px" :disabled="readonly">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="信用等级" required>
|
||||
<el-input v-model="standardForm.creditLevel" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="得分率(%)" required>
|
||||
<div class="range-input">
|
||||
<el-input-number v-model="standardForm.scoreRateLower" :min="0" :max="100" :precision="0" />
|
||||
<span>至</span>
|
||||
<el-input-number v-model="standardForm.scoreRateUpper" :min="0" :max="100" :precision="0" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="得分率每增加" required>
|
||||
<el-input-number v-model="standardForm.scoreRateIncrease" :min="0" :precision="0" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="最大资金使用额度(万元)" required>
|
||||
<div class="range-input">
|
||||
<el-input-number v-model="standardForm.creditLimitLower" :min="0" :precision="2" />
|
||||
<span>至</span>
|
||||
<el-input-number v-model="standardForm.creditLimitUpper" :min="0" :precision="2" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="额度增加" required>
|
||||
<el-input-number v-model="standardForm.creditLimitIncrease" :min="0" :precision="2" />
|
||||
<span class="score-unit">万元</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="标准说明">
|
||||
<el-input v-model="standardForm.standardDescription" type="textarea" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="standardBox = false">取消</el-button>
|
||||
<el-button type="primary" v-if="!readonly" @click="saveStandard">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getList,
|
||||
getDetail,
|
||||
submit,
|
||||
remove,
|
||||
publish,
|
||||
changeStatus,
|
||||
} from '@/api/vehicle/credit-score-quantification';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
data: [],
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
configBox: false,
|
||||
categoryBox: false,
|
||||
itemBox: false,
|
||||
standardBox: false,
|
||||
excelBox: false,
|
||||
readonly: false,
|
||||
excelForm: {},
|
||||
configForm: this.emptyConfig(),
|
||||
currentCategory: { items: [] },
|
||||
currentItemIndex: -1,
|
||||
currentStandardIndex: -1,
|
||||
itemForm: this.emptyItem(),
|
||||
standardForm: this.emptyStandard(),
|
||||
statusMap: {
|
||||
1: { label: '正常', type: 'success' },
|
||||
2: { label: '停用', type: 'info' },
|
||||
3: { label: '草稿', type: 'warning' },
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 760,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
editBtn: false,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 300,
|
||||
column: [
|
||||
{
|
||||
label: '评定表名称',
|
||||
prop: 'name',
|
||||
slot: true,
|
||||
minWidth: 180,
|
||||
search: true,
|
||||
maxlength: 30,
|
||||
rules: [{ required: true, message: '请输入评定表名称', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
minWidth: 260,
|
||||
overHidden: true,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
type: 'select',
|
||||
search: true,
|
||||
slot: true,
|
||||
dataType: 'number',
|
||||
dicData: [
|
||||
{ label: '正常', value: 1 },
|
||||
{ label: '停用', value: 2 },
|
||||
{ label: '草稿', value: 3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
search: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
excelOption: {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '模板上传',
|
||||
prop: 'excelFile',
|
||||
type: 'upload',
|
||||
drag: true,
|
||||
loadText: '模板上传中,请稍等',
|
||||
span: 24,
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
action: '/blade-transport/credit-score-quantification/import-credit-score-quantification',
|
||||
},
|
||||
{
|
||||
label: '模板下载',
|
||||
prop: 'excelTemplate',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
emptyConfig() {
|
||||
return {
|
||||
name: '',
|
||||
remark: '',
|
||||
standardDescription: '',
|
||||
status: 3,
|
||||
categories: this.defaultCategories(),
|
||||
standards: [],
|
||||
};
|
||||
},
|
||||
defaultCategories() {
|
||||
return [
|
||||
{ categoryCode: 'basic', categoryName: '基础得分项', sort: 1, items: [] },
|
||||
{ categoryCode: 'plus', categoryName: '加分项目', sort: 2, items: [] },
|
||||
{ categoryCode: 'minus', categoryName: '减分项目', sort: 3, items: [] },
|
||||
];
|
||||
},
|
||||
emptyItem() {
|
||||
return {
|
||||
itemName: '',
|
||||
scoreDescription: '',
|
||||
optionDescription: '',
|
||||
options: [{ optionName: '', score: 0 }],
|
||||
};
|
||||
},
|
||||
emptyStandard() {
|
||||
return {
|
||||
creditLevel: '',
|
||||
scoreRateLower: undefined,
|
||||
scoreRateUpper: undefined,
|
||||
scoreRateIncrease: undefined,
|
||||
creditLimitLower: undefined,
|
||||
creditLimitUpper: undefined,
|
||||
creditLimitIncrease: undefined,
|
||||
standardDescription: '',
|
||||
};
|
||||
},
|
||||
normalizeDetail(detail) {
|
||||
const categories = this.defaultCategories();
|
||||
const map = {};
|
||||
(detail.categories || []).forEach(category => {
|
||||
map[category.categoryCode] = {
|
||||
...category,
|
||||
items: (category.items || []).map(item => ({
|
||||
...item,
|
||||
options: item.options && item.options.length ? item.options : [{ optionName: '', score: 0 }],
|
||||
})),
|
||||
};
|
||||
});
|
||||
return {
|
||||
...detail,
|
||||
categories: categories.map(category => map[category.categoryCode] || category),
|
||||
standards: detail.standards || [],
|
||||
};
|
||||
},
|
||||
openConfig(row, readonly = false) {
|
||||
this.readonly = readonly;
|
||||
if (row && row.id) {
|
||||
getDetail(row.id).then(res => {
|
||||
this.configForm = this.normalizeDetail(res.data.data);
|
||||
this.configBox = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.configForm = this.emptyConfig();
|
||||
if (row && row.name) {
|
||||
this.configForm.name = row.name;
|
||||
}
|
||||
this.configBox = true;
|
||||
},
|
||||
resetConfig() {
|
||||
this.readonly = false;
|
||||
this.configForm = this.emptyConfig();
|
||||
},
|
||||
openCategory(category) {
|
||||
this.currentCategory = category;
|
||||
this.categoryBox = true;
|
||||
},
|
||||
openItem(row, index = -1) {
|
||||
this.currentItemIndex = index;
|
||||
this.itemForm = row ? JSON.parse(JSON.stringify(row)) : this.emptyItem();
|
||||
this.itemBox = true;
|
||||
},
|
||||
saveItem() {
|
||||
if (!this.itemForm.itemName || !this.itemForm.itemName.trim()) {
|
||||
this.$message.warning('请输入评分项目');
|
||||
return;
|
||||
}
|
||||
if (!this.itemForm.options.length) {
|
||||
this.$message.warning('至少添加1条档位选项');
|
||||
return;
|
||||
}
|
||||
const item = JSON.parse(JSON.stringify(this.itemForm));
|
||||
if (this.currentItemIndex >= 0) {
|
||||
this.currentCategory.items.splice(this.currentItemIndex, 1, item);
|
||||
} else {
|
||||
this.currentCategory.items.push(item);
|
||||
}
|
||||
this.itemBox = false;
|
||||
},
|
||||
removeItem(index) {
|
||||
this.currentCategory.items.splice(index, 1);
|
||||
},
|
||||
addOption() {
|
||||
this.itemForm.options.push({ optionName: '', score: 0 });
|
||||
},
|
||||
removeOption(index) {
|
||||
this.itemForm.options.splice(index, 1);
|
||||
},
|
||||
openStandard(row, index = -1) {
|
||||
this.currentStandardIndex = index;
|
||||
this.standardForm = row ? JSON.parse(JSON.stringify(row)) : this.emptyStandard();
|
||||
this.standardBox = true;
|
||||
},
|
||||
saveStandard() {
|
||||
if (!this.standardForm.creditLevel || !this.standardForm.creditLevel.trim()) {
|
||||
this.$message.warning('请输入信用等级');
|
||||
return;
|
||||
}
|
||||
if (
|
||||
[
|
||||
this.standardForm.scoreRateLower,
|
||||
this.standardForm.scoreRateUpper,
|
||||
this.standardForm.scoreRateIncrease,
|
||||
this.standardForm.creditLimitLower,
|
||||
this.standardForm.creditLimitUpper,
|
||||
this.standardForm.creditLimitIncrease,
|
||||
].some(value => value === undefined || value === null || value === '')
|
||||
) {
|
||||
this.$message.warning('请完整填写评估标准必填项');
|
||||
return;
|
||||
}
|
||||
if (Number(this.standardForm.scoreRateLower) >= Number(this.standardForm.scoreRateUpper)) {
|
||||
this.$message.warning('得分率下限必须小于上限');
|
||||
return;
|
||||
}
|
||||
const standard = JSON.parse(JSON.stringify(this.standardForm));
|
||||
if (this.currentStandardIndex >= 0) {
|
||||
this.configForm.standards.splice(this.currentStandardIndex, 1, standard);
|
||||
} else {
|
||||
this.configForm.standards.push(standard);
|
||||
}
|
||||
this.standardBox = false;
|
||||
},
|
||||
removeStandard(index) {
|
||||
this.configForm.standards.splice(index, 1);
|
||||
},
|
||||
validateConfig() {
|
||||
if (!this.configForm.name || !this.configForm.name.trim()) {
|
||||
this.$message.warning('请输入评定表名称');
|
||||
return false;
|
||||
}
|
||||
const basic = this.configForm.categories.find(item => item.categoryCode === 'basic');
|
||||
if (!basic || basic.items.length === 0) {
|
||||
this.$message.warning('至少配置1个基础得分项目');
|
||||
return false;
|
||||
}
|
||||
for (const category of this.configForm.categories) {
|
||||
for (const item of category.items) {
|
||||
if (!item.options || item.options.length === 0) {
|
||||
this.$message.warning(`${item.itemName}至少存在1条档位选项`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
saveConfig() {
|
||||
if (!this.validateConfig()) return;
|
||||
submit(this.configForm).then(() => {
|
||||
this.configBox = false;
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('仅草稿状态量表可删除,确定删除所选数据?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handlePublish(row) {
|
||||
this.$confirm('发布前将校验所有评分项目、档位选项和评估标准,是否继续?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => publish(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '发布成功!' });
|
||||
});
|
||||
},
|
||||
handleStatus(row) {
|
||||
const nextStatus = row.status === 1 ? 2 : 1;
|
||||
const actionName = nextStatus === 1 ? '启用' : '停用';
|
||||
this.$confirm(`是否确认${actionName}该量表?`, {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => changeStatus(row.id, nextStatus))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出评分量化表数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob(
|
||||
'/blade-transport/credit-score-quantification/export-credit-score-quantification',
|
||||
this.buildExportParams()
|
||||
)
|
||||
.then(res => {
|
||||
downloadXls(res.data, `评分量化表${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/credit-score-quantification/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '评分量化表模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.score-config {
|
||||
padding: 4px 12px 12px;
|
||||
}
|
||||
|
||||
.base-form {
|
||||
max-width: 1080px;
|
||||
margin: 10px auto 28px;
|
||||
}
|
||||
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 24px 0 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 18px 0 12px;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.standard-head {
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
.config-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.category-line {
|
||||
margin: 0 0 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.option-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.option-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(260px, 1fr) 190px 32px 70px;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.range-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.score-unit {
|
||||
margin-left: 8px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
:deep(.score-dialog .el-dialog__body) {
|
||||
max-height: 70vh;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
1243
src/views/vehicle/customer-archive.vue
Normal file
1243
src/views/vehicle/customer-archive.vue
Normal file
File diff suppressed because it is too large
Load Diff
413
src/views/vehicle/etc-record.vue
Normal file
413
src/views/vehicle/etc-record.vue
Normal file
@@ -0,0 +1,413 @@
|
||||
<template>
|
||||
<basic-container class="etc-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('etc_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('etc_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('etc_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('etc_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="输入车牌号模糊查询选择"
|
||||
value-key="value"
|
||||
class="etc-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="ETC记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/etc-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/etc-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('etc_record_add'),
|
||||
viewBtn: this.hasPermission('etc_record_view'),
|
||||
delBtn: this.hasPermission('etc_record_delete'),
|
||||
editBtn: this.hasPermission('etc_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
getVehicleList(1, 20, { plateNo: queryString })
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(
|
||||
records.map(item => ({
|
||||
value: item.plateNo,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
isEmpty(value) {
|
||||
return value === undefined || value === null || value === '';
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
values.dataSource = values.dataSource || '手工录入';
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
if (values.etcCardNo) {
|
||||
values.etcCardNo = values.etcCardNo.trim();
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateDecimal(value, label, required = false) {
|
||||
if (this.isEmpty(value)) {
|
||||
if (required) {
|
||||
this.$message.warning(`请输入${label}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (Number(value) < 0) {
|
||||
this.$message.warning(`${label}不能小于 0`);
|
||||
return false;
|
||||
}
|
||||
if (!/^\d+(\.\d{1,2})?$/.test(String(value))) {
|
||||
this.$message.warning(`${label}最多保留 2 位小数`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (row.entryTime && row.exitTime && new Date(row.exitTime) <= new Date(row.entryTime)) {
|
||||
this.$message.warning('出口时间应大于入口时间');
|
||||
return false;
|
||||
}
|
||||
if (!this.validateDecimal(row.transactionAmount, '交易金额', true)) return false;
|
||||
if (row.remark && row.remark.length > 200) {
|
||||
this.$message.warning('备注不能超过 200 字');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form = { dataSource: '手工录入' };
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出ETC记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/etc-record/export-etc-record', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `ETC记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(`/blade-transport/etc-record/export-template?${this.website.tokenHeader}=${getToken()}`).then(res => {
|
||||
downloadXls(res.data, 'ETC记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.etc-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
449
src/views/vehicle/insurance-record.vue
Normal file
449
src/views/vehicle/insurance-record.vue
Normal file
@@ -0,0 +1,449 @@
|
||||
<template>
|
||||
<basic-container class="insurance-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('insurance_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('insurance_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('insurance_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('insurance_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="输入车牌号/船号模糊查询选择"
|
||||
value-key="value"
|
||||
class="insurance-record-page__vehicle-input"
|
||||
/>
|
||||
</template>
|
||||
<template #policyFileForm>
|
||||
<div class="insurance-record-page__upload">
|
||||
<div class="insurance-record-page__upload-icon">OCR</div>
|
||||
<div class="insurance-record-page__upload-text">
|
||||
<strong>上传图片,自动识别填写</strong>
|
||||
<span>支持JPG、PNG、PDF上传</span>
|
||||
</div>
|
||||
<el-upload
|
||||
:action="recognizeUrl"
|
||||
:data="recognizeData"
|
||||
:headers="uploadHeaders"
|
||||
:show-file-list="false"
|
||||
:on-success="handleRecognizeSuccess"
|
||||
:on-error="handleRecognizeError"
|
||||
accept=".jpg,.jpeg,.png,.pdf"
|
||||
v-if="hasPermission('insurance_record_ocr')"
|
||||
>
|
||||
<el-button type="primary" plain>上传文件</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="保险记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/insurance-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/insurance-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('insurance_record_add'),
|
||||
viewBtn: this.hasPermission('insurance_record_view'),
|
||||
delBtn: this.hasPermission('insurance_record_delete'),
|
||||
editBtn: this.hasPermission('insurance_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
recognizeUrl() {
|
||||
return '/blade-transport/insurance-record/recognize';
|
||||
},
|
||||
recognizeData() {
|
||||
return {
|
||||
vehicleType: this.form.vehicleType || '车辆',
|
||||
ocrTemplate: this.form.ocrTemplate,
|
||||
};
|
||||
},
|
||||
uploadHeaders() {
|
||||
return {
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
const vehicleType = this.form.vehicleType || '车辆';
|
||||
const request = vehicleType === '船舶' ? getShipList : getVehicleList;
|
||||
const params =
|
||||
vehicleType === '船舶'
|
||||
? { shipIdentifierNo: queryString }
|
||||
: { plateNo: queryString };
|
||||
request(1, 20, params)
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(
|
||||
records.map(item => ({
|
||||
value: vehicleType === '船舶' ? item.shipIdentifierNo || item.shipName : item.plateNo,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
if (values.policyNo) {
|
||||
values.policyNo = values.policyNo.trim();
|
||||
}
|
||||
return values;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (row.startDate && row.endDate && row.endDate < row.startDate) {
|
||||
this.$message.warning('结束日期不能早于开始日期');
|
||||
return false;
|
||||
}
|
||||
if (Number(row.insuredAmount) < 0 || Number(row.premium) < 0) {
|
||||
this.$message.warning('保额、保费不能小于 0');
|
||||
return false;
|
||||
}
|
||||
if (row.remark && row.remark.length > 200) {
|
||||
this.$message.warning('备注最多 200 个字');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form.vehicleType = '车辆';
|
||||
this.form.ocrTemplate = '紫金-机动车交强险';
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出保险记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/insurance-record/export-insurance-record', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `保险记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/insurance-record/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '保险记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
handleRecognizeSuccess(res) {
|
||||
if (res && res.success && res.data) {
|
||||
this.form = {
|
||||
...this.form,
|
||||
...res.data,
|
||||
vehicleType: res.data.vehicleType || this.form.vehicleType || '车辆',
|
||||
};
|
||||
this.$message.success('识别完成');
|
||||
} else {
|
||||
this.$message.warning((res && res.msg) || '识别失败,请手动填写');
|
||||
}
|
||||
},
|
||||
handleRecognizeError() {
|
||||
this.$message.warning('识别失败,请手动填写');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.insurance-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__vehicle-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__upload {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
min-height: 88px;
|
||||
padding: 18px 24px;
|
||||
border: 1px dashed #8c8c8c;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
&__upload-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
color: #606266;
|
||||
border: 2px solid #8c8c8c;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__upload-text {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
color: #303133;
|
||||
|
||||
span {
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
602
src/views/vehicle/maintenance-plan.vue
Normal file
602
src/views/vehicle/maintenance-plan.vue
Normal file
@@ -0,0 +1,602 @@
|
||||
<template>
|
||||
<basic-container class="maintenance-plan-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('maintenance_plan_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('maintenance_plan_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('maintenance_plan_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('maintenance_plan_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #mileage="{ row }">
|
||||
<span>{{ formatMileage(row.mileage, row.mileageUnit) }}</span>
|
||||
</template>
|
||||
<template #nextMaintenanceMileage="{ row }">
|
||||
<span>{{ formatMileage(row.nextMaintenanceMileage, row.mileageUnit) }}</span>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="保养记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove } from '@/api/vehicle/maintenance-plan';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const validateNonNegative = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < 0) {
|
||||
callback(new Error('不能小于 0'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
prop: 'vehicleType',
|
||||
type: 'radio',
|
||||
minWidth: 110,
|
||||
dicData: [
|
||||
{ label: '车辆', value: '车辆' },
|
||||
{ label: '船舶', value: '船舶' },
|
||||
],
|
||||
value: '车辆',
|
||||
search: true,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '车牌号/船号',
|
||||
prop: 'vehicleNo',
|
||||
slot: true,
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '保养人',
|
||||
prop: 'maintainer',
|
||||
rules: [{ max: 20, message: '最多 20 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '保养时间',
|
||||
prop: 'maintenanceTime',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
rules: [{ required: true, message: '请选择保养时间', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '里程/航程数',
|
||||
prop: 'mileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 130,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '里程单位',
|
||||
prop: 'mileageUnit',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '公里', value: '公里' },
|
||||
{ label: '海里', value: '海里' },
|
||||
],
|
||||
value: '公里',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '保养项目',
|
||||
prop: 'maintenanceItem',
|
||||
minWidth: 160,
|
||||
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '费用',
|
||||
prop: 'cost',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
rules: [
|
||||
{ required: true, message: '请输入费用', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '店名',
|
||||
prop: 'storeName',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '联系电话',
|
||||
prop: 'contactPhone',
|
||||
minWidth: 150,
|
||||
rules: [{ max: 20, message: '最多 20 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '地址',
|
||||
prop: 'address',
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
span: 24,
|
||||
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '下次保养时间',
|
||||
prop: 'nextMaintenanceTime',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '下次保养里程/航程',
|
||||
prop: 'nextMaintenanceMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 170,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
display: false,
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '组织',
|
||||
prop: 'createDept',
|
||||
type: 'tree',
|
||||
dicData: [],
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
},
|
||||
checkStrictly: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '更新人',
|
||||
prop: 'updateUserName',
|
||||
formatter: formatUpdateUserName,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 170,
|
||||
},
|
||||
],
|
||||
},
|
||||
excelOption: {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '模板上传',
|
||||
prop: 'excelFile',
|
||||
type: 'upload',
|
||||
drag: true,
|
||||
loadText: '模板上传中,请稍等',
|
||||
span: 24,
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
action: '/blade-transport/maintenance-plan/import-maintenance-plan',
|
||||
},
|
||||
{
|
||||
label: '模板下载',
|
||||
prop: 'excelTemplate',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('maintenance_plan_add'),
|
||||
viewBtn: this.hasPermission('maintenance_plan_view'),
|
||||
delBtn: this.hasPermission('maintenance_plan_delete'),
|
||||
editBtn: this.hasPermission('maintenance_plan_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
formatMileage(value, unit) {
|
||||
if (value === undefined || value === null || value === '') return '';
|
||||
return `${Math.max(Number(value), 0)} ${unit || '公里'}`;
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个';
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
if (!values.mileageUnit) {
|
||||
values.mileageUnit = values.vehicleType === '船舶' ? '海里' : '公里';
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(this.normalizeRow(row)).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(this.normalizeRow(row)).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出保养记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/maintenance-plan/export-maintenance-plan', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `保养记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/maintenance-plan/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '保养记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.maintenance-plan-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
598
src/views/vehicle/maintenance-record.vue
Normal file
598
src/views/vehicle/maintenance-record.vue
Normal file
@@ -0,0 +1,598 @@
|
||||
<template>
|
||||
<basic-container class="maintenance-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('maintenance_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('maintenance_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('maintenance_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('maintenance_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #mileage="{ row }">
|
||||
<span>{{ formatMileage(row.mileage, row.mileageUnit) }}</span>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="维修记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove } from '@/api/vehicle/maintenance-record';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const validateNonNegative = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < 0) {
|
||||
callback(new Error('不能小于 0'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
prop: 'vehicleType',
|
||||
type: 'radio',
|
||||
minWidth: 110,
|
||||
dicData: [
|
||||
{ label: '车辆', value: '车辆' },
|
||||
{ label: '船舶', value: '船舶' },
|
||||
],
|
||||
value: '车辆',
|
||||
search: true,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '车牌号/船号',
|
||||
prop: 'vehicleNo',
|
||||
slot: true,
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '维修人',
|
||||
prop: 'maintainer',
|
||||
rules: [{ max: 20, message: '最多 20 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '维修时间',
|
||||
prop: 'maintenanceTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
rules: [{ required: true, message: '请选择维修时间', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '维修位置',
|
||||
prop: 'location',
|
||||
minWidth: 160,
|
||||
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '更换零件',
|
||||
prop: 'replacedPart',
|
||||
minWidth: 160,
|
||||
overHidden: true,
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '费用',
|
||||
prop: 'cost',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
rules: [
|
||||
{ required: true, message: '请输入费用', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '维修单位',
|
||||
prop: 'company',
|
||||
minWidth: 180,
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '联系方式',
|
||||
prop: 'contact',
|
||||
minWidth: 150,
|
||||
rules: [{ max: 20, message: '最多 20 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '出厂时间',
|
||||
prop: 'factoryTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '里程/航程数',
|
||||
prop: 'mileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
minWidth: 130,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '里程单位',
|
||||
prop: 'mileageUnit',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '公里', value: '公里' },
|
||||
{ label: '海里', value: '海里' },
|
||||
],
|
||||
value: '公里',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '地址',
|
||||
prop: 'address',
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
span: 24,
|
||||
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
hide: true,
|
||||
rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
display: false,
|
||||
minWidth: 170,
|
||||
},
|
||||
{
|
||||
label: '创建开始时间',
|
||||
prop: 'createTimeStart',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '创建结束时间',
|
||||
prop: 'createTimeEnd',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '组织',
|
||||
prop: 'createDept',
|
||||
type: 'tree',
|
||||
dicData: [],
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
},
|
||||
checkStrictly: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '更新人',
|
||||
prop: 'updateUserName',
|
||||
formatter: formatUpdateUserName,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 170,
|
||||
},
|
||||
],
|
||||
},
|
||||
excelOption: {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '模板上传',
|
||||
prop: 'excelFile',
|
||||
type: 'upload',
|
||||
drag: true,
|
||||
loadText: '模板上传中,请稍等',
|
||||
span: 24,
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
action: '/blade-transport/maintenance-record/import-maintenance-record',
|
||||
},
|
||||
{
|
||||
label: '模板下载',
|
||||
prop: 'excelTemplate',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('maintenance_record_add'),
|
||||
viewBtn: this.hasPermission('maintenance_record_view'),
|
||||
delBtn: this.hasPermission('maintenance_record_delete'),
|
||||
editBtn: this.hasPermission('maintenance_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
formatMileage(value, unit) {
|
||||
if (value === undefined || value === null || value === '') return '';
|
||||
return `${Math.max(Number(value), 0)} ${unit || '公里'}`;
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个';
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
if (!values.mileageUnit) {
|
||||
values.mileageUnit = values.vehicleType === '船舶' ? '海里' : '公里';
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(this.normalizeRow(row)).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(this.normalizeRow(row)).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出维修记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/maintenance-record/export-maintenance-record', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `维修记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/maintenance-record/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '维修记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.maintenance-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
431
src/views/vehicle/mileage-record.vue
Normal file
431
src/views/vehicle/mileage-record.vue
Normal file
@@ -0,0 +1,431 @@
|
||||
<template>
|
||||
<basic-container class="mileage-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('mileage_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('mileage_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('mileage_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('mileage_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
:disabled="Boolean(form.id)"
|
||||
clearable
|
||||
placeholder="输入车牌号模糊查询选择"
|
||||
value-key="value"
|
||||
class="mileage-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="里程记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/mileage-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/mileage-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('mileage_record_add'),
|
||||
viewBtn: this.hasPermission('mileage_record_view'),
|
||||
delBtn: this.hasPermission('mileage_record_delete'),
|
||||
editBtn: this.hasPermission('mileage_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
getVehicleList(1, 20, { plateNo: queryString })
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(records.map(item => ({ value: item.plateNo })));
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
isEmpty(value) {
|
||||
return value === undefined || value === null || value === '';
|
||||
},
|
||||
toNumber(value) {
|
||||
return this.isEmpty(value) ? null : Number(value);
|
||||
},
|
||||
toMoney(value) {
|
||||
return Math.round(Number(value) * 100) / 100;
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
const previousMonthMileage = this.toNumber(values.previousMonthMileage);
|
||||
const currentMonthMileage = this.toNumber(values.currentMonthMileage);
|
||||
if (this.isEmpty(values.monthlyMileage) && previousMonthMileage !== null && currentMonthMileage !== null) {
|
||||
values.monthlyMileage = this.toMoney(currentMonthMileage - previousMonthMileage);
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateMileage(value, label) {
|
||||
if (this.isEmpty(value)) {
|
||||
return true;
|
||||
}
|
||||
if (Number(value) < 0) {
|
||||
this.$message.warning(`${label}不能小于 0`);
|
||||
return false;
|
||||
}
|
||||
if (!/^\d+(\.\d{1,2})?$/.test(String(value))) {
|
||||
this.$message.warning(`${label}最多保留 2 位小数`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateRow(row) {
|
||||
const mileageFields = [
|
||||
['previousMonthMileage', '上月统计里程'],
|
||||
['currentMonthMileage', '本月统计里程'],
|
||||
['monthlyMileage', '本月行驶里程'],
|
||||
['totalMileage', '累计行驶里程'],
|
||||
];
|
||||
if (!mileageFields.every(([prop, label]) => this.validateMileage(row[prop], label))) {
|
||||
return false;
|
||||
}
|
||||
const previousMonthMileage = this.toNumber(row.previousMonthMileage);
|
||||
const currentMonthMileage = this.toNumber(row.currentMonthMileage);
|
||||
const monthlyMileage = this.toNumber(row.monthlyMileage);
|
||||
const totalMileage = this.toNumber(row.totalMileage);
|
||||
if (
|
||||
previousMonthMileage !== null &&
|
||||
currentMonthMileage !== null &&
|
||||
monthlyMileage !== null &&
|
||||
this.toMoney(currentMonthMileage - previousMonthMileage) !== this.toMoney(monthlyMileage)
|
||||
) {
|
||||
this.$message.warning('本月行驶里程应等于本月统计里程减去上月统计里程');
|
||||
return false;
|
||||
}
|
||||
if (totalMileage !== null && currentMonthMileage !== null && totalMileage < currentMonthMileage) {
|
||||
this.$message.warning('累计行驶里程应大于等于本月统计里程');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
const vehicleNoColumn = this.findColumn(this.option.column, 'vehicleNo');
|
||||
vehicleNoColumn.disabled = type !== 'add';
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出里程记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/mileage-record/export-mileage-record', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `里程记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(`/blade-transport/mileage-record/export-template?${this.website.tokenHeader}=${getToken()}`).then(
|
||||
res => {
|
||||
downloadXls(res.data, '里程记录模板.xlsx');
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mileage-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
450
src/views/vehicle/oil-electric-record.vue
Normal file
450
src/views/vehicle/oil-electric-record.vue
Normal file
@@ -0,0 +1,450 @@
|
||||
<template>
|
||||
<basic-container class="oil-electric-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('oil_electric_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('oil_electric_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('oil_electric_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('oil_electric_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="输入车牌号/船号查询选择"
|
||||
value-key="value"
|
||||
class="oil-electric-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="油电记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/oil-electric-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/oil-electric-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('oil_electric_record_add'),
|
||||
viewBtn: this.hasPermission('oil_electric_record_view'),
|
||||
delBtn: this.hasPermission('oil_electric_record_delete'),
|
||||
editBtn: this.hasPermission('oil_electric_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'form.vehicleType': {
|
||||
handler() {
|
||||
this.updateVehicleType();
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
'form.feeType': {
|
||||
handler(value) {
|
||||
this.updateFeeType(value || '加油');
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
updateVehicleType() {
|
||||
if (!this.form.id && this.form.vehicleNo) {
|
||||
this.form.vehicleNo = '';
|
||||
}
|
||||
},
|
||||
updateFeeType(feeType) {
|
||||
const oilProductColumn = this.findColumn(this.option.column, 'oilProduct');
|
||||
const unitPriceColumn = this.findColumn(this.option.column, 'unitPrice');
|
||||
oilProductColumn.display = feeType !== '充电';
|
||||
unitPriceColumn.append = feeType === '充电' ? '元/度' : '元/升';
|
||||
if (feeType === '充电') {
|
||||
this.form.oilProduct = undefined;
|
||||
}
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
const vehicleType = this.form.vehicleType || '车辆';
|
||||
const request = vehicleType === '船舶' ? getShipList : getVehicleList;
|
||||
const params =
|
||||
vehicleType === '船舶' ? { shipIdentifierNo: queryString } : { plateNo: queryString };
|
||||
request(1, 20, params)
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(
|
||||
records.map(item => ({
|
||||
value: vehicleType === '船舶' ? item.shipIdentifierNo || item.shipName : item.plateNo,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
isEmpty(value) {
|
||||
return value === undefined || value === null || value === '';
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
values.vehicleType = values.vehicleType || '车辆';
|
||||
values.dataSource = values.dataSource || '手工录入';
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleType === '船舶' ? values.vehicleNo.trim() : values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
if (values.feeType === '充电') {
|
||||
values.oilProduct = undefined;
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateDecimal(value, label, required = false) {
|
||||
if (this.isEmpty(value)) {
|
||||
if (required) {
|
||||
this.$message.warning(`请输入${label}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (Number(value) < 0) {
|
||||
this.$message.warning(`${label}不能小于 0`);
|
||||
return false;
|
||||
}
|
||||
if (!/^\d+(\.\d{1,2})?$/.test(String(value))) {
|
||||
this.$message.warning(`${label}最多保留 2 位小数`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (!this.validateDecimal(row.transactionAmount, '交易金额', true)) return false;
|
||||
if (!this.validateDecimal(row.quantity, '数量')) return false;
|
||||
if (!this.validateDecimal(row.unitPrice, '单价')) return false;
|
||||
if (!this.validateDecimal(row.balance, '余额')) return false;
|
||||
if (row.remark && row.remark.length > 200) {
|
||||
this.$message.warning('备注不能超过 200 字');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form = { vehicleType: '车辆', feeType: '加油', dataSource: '手工录入' };
|
||||
this.updateFeeType('加油');
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
this.updateFeeType(detail.feeType);
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出油电记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/oil-electric-record/export-oil-electric-record', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `油电记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(`/blade-transport/oil-electric-record/export-template?${this.website.tokenHeader}=${getToken()}`).then(
|
||||
res => {
|
||||
downloadXls(res.data, '油电记录模板.xlsx');
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.oil-electric-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
441
src/views/vehicle/other-expense-record.vue
Normal file
441
src/views/vehicle/other-expense-record.vue
Normal file
@@ -0,0 +1,441 @@
|
||||
<template>
|
||||
<basic-container class="other-expense-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('other_expense_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('other_expense_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('other_expense_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('other_expense_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="输入车牌号/船号查询选择"
|
||||
value-key="value"
|
||||
class="other-expense-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="其他费用记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/other-expense-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/other-expense-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('other_expense_record_add'),
|
||||
viewBtn: this.hasPermission('other_expense_record_view'),
|
||||
delBtn: this.hasPermission('other_expense_record_delete'),
|
||||
editBtn: this.hasPermission('other_expense_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'form.vehicleType': {
|
||||
handler() {
|
||||
this.updateVehicleType();
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
updateVehicleType() {
|
||||
if (!this.form.id && this.form.vehicleNo) {
|
||||
this.form.vehicleNo = '';
|
||||
}
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
const vehicleType = this.form.vehicleType || '车辆';
|
||||
const request = vehicleType === '船舶' ? getShipList : getVehicleList;
|
||||
const params =
|
||||
vehicleType === '船舶' ? { shipIdentifierNo: queryString } : { plateNo: queryString };
|
||||
request(1, 20, params)
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(
|
||||
records.map(item => ({
|
||||
value: vehicleType === '船舶' ? item.shipIdentifierNo || item.shipName : item.plateNo,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
isEmpty(value) {
|
||||
return value === undefined || value === null || value === '';
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
values.vehicleType = values.vehicleType || '车辆';
|
||||
values.dataSource = values.dataSource || '手工录入';
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleType === '船舶' ? values.vehicleNo.trim() : values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateDecimal(value, label, required = false) {
|
||||
if (this.isEmpty(value)) {
|
||||
if (required) {
|
||||
this.$message.warning(`请输入${label}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (Number(value) < 0) {
|
||||
this.$message.warning(`${label}不能小于 0`);
|
||||
return false;
|
||||
}
|
||||
if (!/^\d+(\.\d{1,2})?$/.test(String(value))) {
|
||||
this.$message.warning(`${label}最多保留 2 位小数`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (!this.validateDecimal(row.amount, '金额', true)) return false;
|
||||
if (row.remark && row.remark.length > 200) {
|
||||
this.$message.warning('备注不能超过 200 字');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form = {
|
||||
vehicleType: '车辆',
|
||||
expenseDate: this.$dayjs().format('YYYY-MM-DD'),
|
||||
dataSource: '手工录入',
|
||||
};
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
buildQuery(params = {}) {
|
||||
const { expenseDateRange } = this.query;
|
||||
const values = { ...params, ...this.query };
|
||||
if (expenseDateRange) {
|
||||
values.expenseDateStart = expenseDateRange[0];
|
||||
values.expenseDateEnd = expenseDateRange[1];
|
||||
values.expenseDateRange = null;
|
||||
}
|
||||
return values;
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, this.buildQuery(params))
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出其他费用记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/other-expense-record/export-other-expense-record', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `其他费用记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.buildQuery(),
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(`/blade-transport/other-expense-record/export-template?${this.website.tokenHeader}=${getToken()}`).then(
|
||||
res => {
|
||||
downloadXls(res.data, '其他费用记录模板.xlsx');
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.other-expense-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
393
src/views/vehicle/tire-replacement-record.vue
Normal file
393
src/views/vehicle/tire-replacement-record.vue
Normal file
@@ -0,0 +1,393 @@
|
||||
<template>
|
||||
<basic-container class="tire-replacement-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('tire_replacement_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('tire_replacement_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('tire_replacement_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('tire_replacement_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="请输入关键字"
|
||||
value-key="value"
|
||||
class="tire-replacement-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="换胎记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/tire-replacement-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/tire-replacement-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('tire_replacement_record_add'),
|
||||
viewBtn: this.hasPermission('tire_replacement_record_view'),
|
||||
delBtn: this.hasPermission('tire_replacement_record_delete'),
|
||||
editBtn: this.hasPermission('tire_replacement_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
getVehicleList(1, 20, { plateNo: queryString })
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(records.map(item => ({ value: item.plateNo })));
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (Number(row.replacementCost) < 0) {
|
||||
this.$message.warning('换胎费用不能小于 0');
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.tireQuantity !== undefined &&
|
||||
row.tireQuantity !== null &&
|
||||
row.tireQuantity !== '' &&
|
||||
(!Number.isInteger(Number(row.tireQuantity)) || Number(row.tireQuantity) <= 0)
|
||||
) {
|
||||
this.$message.warning('换胎数量必须为正整数');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form = {};
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出换胎记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob(
|
||||
'/blade-transport/tire-replacement-record/export-tire-replacement-record',
|
||||
this.buildExportParams()
|
||||
)
|
||||
.then(res => {
|
||||
downloadXls(res.data, `换胎记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/tire-replacement-record/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '换胎记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tire-replacement-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
411
src/views/vehicle/transport-change-record.vue
Normal file
411
src/views/vehicle/transport-change-record.vue
Normal file
@@ -0,0 +1,411 @@
|
||||
<template>
|
||||
<basic-container class="transport-change-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('transport_change_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('transport_change_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('transport_change_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('transport_change_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button v-if="row.vehicleNo" type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="输入车牌号/船号模糊查询选择"
|
||||
value-key="value"
|
||||
class="transport-change-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="变更记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/transport-change-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/transport-change-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('transport_change_record_add'),
|
||||
viewBtn: this.hasPermission('transport_change_record_view'),
|
||||
delBtn: this.hasPermission('transport_change_record_delete'),
|
||||
editBtn: this.hasPermission('transport_change_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'form.vehicleType': {
|
||||
handler(value) {
|
||||
this.updateVehicleType(value || '车辆');
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
updateVehicleType() {
|
||||
if (!this.form.id && this.form.vehicleNo) {
|
||||
this.form.vehicleNo = '';
|
||||
}
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
const vehicleType = this.form.vehicleType || '车辆';
|
||||
const request = vehicleType === '船舶' ? getShipList : getVehicleList;
|
||||
const params =
|
||||
vehicleType === '船舶' ? { shipIdentifierNo: queryString } : { plateNo: queryString };
|
||||
request(1, 20, params)
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(
|
||||
records.map(item => ({
|
||||
value: vehicleType === '船舶' ? item.shipIdentifierNo || item.shipName : item.plateNo,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
values.vehicleType = values.vehicleType || '车辆';
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleType === '船舶' ? values.vehicleNo.trim() : values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (row.changeContent && row.changeContent.length > 200) {
|
||||
this.$message.warning('变更内容不能超过 200 字');
|
||||
return false;
|
||||
}
|
||||
if (row.remark && row.remark.length > 200) {
|
||||
this.$message.warning('备注不能超过 200 字');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form = { vehicleType: '车辆' };
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出变更记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob(
|
||||
'/blade-transport/transport-change-record/export-transport-change-record',
|
||||
this.buildExportParams()
|
||||
)
|
||||
.then(res => {
|
||||
downloadXls(res.data, `变更记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/transport-change-record/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '变更记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.transport-change-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
490
src/views/vehicle/violation-record.vue
Normal file
490
src/views/vehicle/violation-record.vue
Normal file
@@ -0,0 +1,490 @@
|
||||
<template>
|
||||
<basic-container class="violation-record-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="hasPermission('violation_record_import')"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('violation_record_template')"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="hasPermission('violation_record_export')"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="hasPermission('violation_record_delete')"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNo="{ row, index }">
|
||||
<el-button type="primary" link @click="$refs.crud.rowView(row, index)">
|
||||
{{ row.vehicleNo }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #vehicleNoForm>
|
||||
<el-autocomplete
|
||||
v-model="form.vehicleNo"
|
||||
:fetch-suggestions="fetchVehicleOptions"
|
||||
clearable
|
||||
placeholder="输入车牌号/船号模糊查询选择"
|
||||
value-key="value"
|
||||
class="violation-record-page__input"
|
||||
/>
|
||||
</template>
|
||||
<template #driverNameForm>
|
||||
<el-autocomplete
|
||||
v-if="form.vehicleType !== '船舶'"
|
||||
v-model="form.driverName"
|
||||
:fetch-suggestions="fetchDriverOptions"
|
||||
clearable
|
||||
placeholder="输入驾驶人模糊查询选择"
|
||||
value-key="value"
|
||||
class="violation-record-page__input"
|
||||
/>
|
||||
<el-input v-else v-model="form.driverName" clearable maxlength="20" placeholder="请输入船长" />
|
||||
</template>
|
||||
<template #processStatus="{ row }">
|
||||
<span :class="row.processStatus === '未处理' ? 'violation-record-page__danger' : ''">
|
||||
{{ row.processStatus }}
|
||||
</span>
|
||||
</template>
|
||||
<template #attachments="{ row }">
|
||||
<span>{{ formatAttachments(row.attachments) }}</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="违章记录数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { add, getDetail, getList, remove, update } from '@/api/vehicle/violation-record';
|
||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||
import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
|
||||
import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { excelOption, option } from '@/option/vehicle/violation-record';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
option,
|
||||
excelOption,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initDeptTree();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.hasPermission('violation_record_add'),
|
||||
viewBtn: this.hasPermission('violation_record_view'),
|
||||
delBtn: this.hasPermission('violation_record_delete'),
|
||||
editBtn: this.hasPermission('violation_record_edit'),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
const ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'form.vehicleType': {
|
||||
handler(value) {
|
||||
this.updateVehicleTypeDisplays(value || '车辆');
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
'form.processStatus': {
|
||||
handler(value) {
|
||||
this.updateProcessResultDisplay(value || '已处理');
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission[code], false);
|
||||
},
|
||||
initDeptTree() {
|
||||
getDeptTree(this.userInfo.tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'createDept');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
updateVehicleTypeDisplays(vehicleType) {
|
||||
const vehicleTypeColumn = this.findColumn(this.option.column, 'vehicleType');
|
||||
const driverColumn = this.findColumn(this.option.column, 'driverName');
|
||||
const typeColumn = this.findColumn(this.option.column, 'violationType');
|
||||
const itemColumn = this.findColumn(this.option.column, 'violationItem');
|
||||
driverColumn.label = vehicleType === '船舶' ? '船长' : '驾驶人';
|
||||
typeColumn.display = vehicleType !== '船舶';
|
||||
itemColumn.display = vehicleType === '船舶';
|
||||
vehicleTypeColumn.disabled = Boolean(this.form.id);
|
||||
if (vehicleType === '船舶') {
|
||||
this.form.violationType = undefined;
|
||||
} else {
|
||||
this.form.violationItem = undefined;
|
||||
}
|
||||
},
|
||||
updateProcessResultDisplay(processStatus) {
|
||||
const column = this.findColumn(this.option.column, 'processResult');
|
||||
column.display = processStatus !== '未处理';
|
||||
if (processStatus === '未处理') {
|
||||
this.form.processResult = undefined;
|
||||
}
|
||||
},
|
||||
fetchVehicleOptions(queryString, callback) {
|
||||
const vehicleType = this.form.vehicleType || '车辆';
|
||||
const request = vehicleType === '船舶' ? getShipList : getVehicleList;
|
||||
const params =
|
||||
vehicleType === '船舶'
|
||||
? { shipIdentifierNo: queryString }
|
||||
: { plateNo: queryString };
|
||||
request(1, 20, params)
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(
|
||||
records.map(item => ({
|
||||
value: vehicleType === '船舶' ? item.shipIdentifierNo || item.shipName : item.plateNo,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
fetchDriverOptions(queryString, callback) {
|
||||
getDriverList(1, 20, { driverName: queryString })
|
||||
.then(res => {
|
||||
const records = res.data.data.records || [];
|
||||
callback(records.map(item => ({ value: item.driverName })));
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
formatAttachments(value) {
|
||||
if (!value) return '';
|
||||
if (Array.isArray(value)) return `${value.length} 个`;
|
||||
if (typeof value !== 'string') return '1 个';
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? `${attachments.length} 个` : '1 个';
|
||||
} catch (error) {
|
||||
return (
|
||||
value
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean).length + ' 个'
|
||||
);
|
||||
}
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value || Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
try {
|
||||
const attachments = JSON.parse(value);
|
||||
return Array.isArray(attachments) ? attachments : [];
|
||||
} catch (error) {
|
||||
return value
|
||||
.split(',')
|
||||
.map(item => ({ name: item.trim(), url: item.trim() }))
|
||||
.filter(item => item.url);
|
||||
}
|
||||
},
|
||||
stringifyAttachments(value) {
|
||||
if (!value || typeof value === 'string') return value;
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
values.vehicleType = values.vehicleType || '车辆';
|
||||
values.processStatus = values.processStatus || '已处理';
|
||||
if (values.vehicleNo) {
|
||||
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
|
||||
}
|
||||
if (values.processStatus === '未处理') {
|
||||
values.processResult = undefined;
|
||||
}
|
||||
if (values.vehicleType === '船舶') {
|
||||
values.violationType = undefined;
|
||||
} else {
|
||||
values.violationItem = undefined;
|
||||
}
|
||||
values.attachments = this.stringifyAttachments(values.attachments);
|
||||
return values;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (row.violationTime && new Date(row.violationTime.replace(/-/g, '/')).getTime() > Date.now()) {
|
||||
this.$message.warning('时间不能超过当前时间');
|
||||
return false;
|
||||
}
|
||||
if (Number(row.fineAmount) < 0) {
|
||||
this.$message.warning('被罚金额不能小于 0');
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
row.deductPoints !== undefined &&
|
||||
row.deductPoints !== null &&
|
||||
row.deductPoints !== '' &&
|
||||
(!Number.isInteger(Number(row.deductPoints)) ||
|
||||
Number(row.deductPoints) < 0 ||
|
||||
Number(row.deductPoints) > 15)
|
||||
) {
|
||||
this.$message.warning('被扣分数范围为 0-15 分');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
add(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const values = this.normalizeRow(row);
|
||||
if (!this.validateRow(values)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
update(values).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (type === 'add') {
|
||||
this.form = {
|
||||
vehicleType: '车辆',
|
||||
processStatus: '已处理',
|
||||
};
|
||||
this.updateVehicleTypeDisplays('车辆');
|
||||
this.updateProcessResultDisplay('已处理');
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = res.data.data;
|
||||
detail.attachments = this.parseAttachments(detail.attachments);
|
||||
this.form = detail;
|
||||
this.updateVehicleTypeDisplays(detail.vehicleType);
|
||||
this.updateProcessResultDisplay(detail.processStatus);
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出违章记录数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-transport/violation-record/export-violation-record', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `违章记录${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
return {
|
||||
...this.query,
|
||||
ids: this.ids,
|
||||
[this.website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-transport/violation-record/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '违章记录模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.violation-record-page {
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__danger {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user