<template>
|
<div class="personal-business-trip-table">
|
<div class="table-header">
|
<h4>出差记录</h4>
|
<div class="header-actions">
|
<el-button
|
size="small"
|
type="primary"
|
@click="handleAddTrip"
|
icon="el-icon-plus"
|
>
|
新增出差
|
</el-button>
|
<el-button size="small" @click="exportToExcel" icon="el-icon-download">
|
导出Excel
|
</el-button>
|
</div>
|
</div>
|
|
<!-- 筛选条件 -->
|
<el-card class="filter-card" shadow="never">
|
<el-form :model="filterForm" inline>
|
<el-form-item label="出差日期">
|
<el-date-picker
|
v-model="filterForm.dateRange"
|
type="daterange"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
value-format="yyyy-MM-dd"
|
/>
|
</el-form-item>
|
<el-form-item label="目的地">
|
<el-input
|
v-model="filterForm.destination"
|
placeholder="输入目的地"
|
clearable
|
/>
|
</el-form-item>
|
<el-form-item label="状态">
|
<el-select v-model="filterForm.status" clearable>
|
<el-option label="全部" value="" />
|
<el-option label="进行中" value="进行中" />
|
<el-option label="已完成" value="已完成" />
|
<el-option label="已取消" value="已取消" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="handleFilter">查询</el-button>
|
<el-button @click="handleReset">重置</el-button>
|
</el-form-item>
|
</el-form>
|
</el-card>
|
|
<el-table
|
:data="filteredTrips"
|
border
|
style="width: 100%"
|
v-loading="loading"
|
class="business-trip-table"
|
>
|
<el-table-column prop="tripNumber" label="出差单号" width="140" fixed>
|
<template #default="scope">
|
<el-tag type="info" size="small">{{ scope.row.tripNumber }}</el-tag>
|
</template>
|
</el-table-column>
|
|
<el-table-column prop="startCity" label="出发城市" >
|
<template #default="scope">
|
<span class="city-cell">{{ scope.row.startCity }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column prop="endCity" label="目的城市" >
|
<template #default="scope">
|
<span class="city-cell">{{ scope.row.endCity }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column prop="startDate" label="开始日期" sortable>
|
<template #default="scope">
|
<span class="date-cell">{{ scope.row.startDate }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column prop="endDate" label="结束日期" sortable>
|
<template #default="scope">
|
<span class="date-cell">{{ scope.row.endDate }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column prop="duration" label="出差天数" >
|
<template #default="scope">
|
<el-tag>{{ scope.row.duration }}天</el-tag>
|
</template>
|
</el-table-column>
|
|
<el-table-column prop="distance" label="里程(km)" sortable>
|
<template #default="scope">
|
<span class="distance-cell">{{ scope.row.distance }}km</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
prop="purpose"
|
label="出差目的"
|
min-width="150"
|
show-overflow-tooltip
|
>
|
<template #default="scope">
|
<span class="purpose-cell">{{ scope.row.purpose }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column prop="status" label="状态" fixed="right">
|
<template #default="scope">
|
<el-tag :type="getStatusType(scope.row.status)" effect="light">
|
{{ scope.row.status }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="操作" width="150" fixed="right">
|
<template #default="scope">
|
<el-button
|
size="mini"
|
type="text"
|
@click="viewTripDetails(scope.row)"
|
icon="el-icon-view"
|
>
|
详情
|
</el-button>
|
<el-button
|
size="mini"
|
type="text"
|
@click="editTrip(scope.row)"
|
icon="el-icon-edit"
|
:disabled="scope.row.status === '已完成'"
|
>
|
编辑
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<!-- 分页 -->
|
<div class="pagination-container">
|
<el-pagination
|
:current-page="currentPage"
|
:page-size="pageSize"
|
:total="totalRecords"
|
layout="total, sizes, prev, pager, next, jumper"
|
@size-change="handleSizeChange"
|
@current-change="handleCurrentChange"
|
/>
|
</div>
|
|
<!-- 出差详情对话框 -->
|
<el-dialog
|
:title="`出差详情 - ${currentTrip ? currentTrip.tripNumber : ''}`"
|
:visible.sync="detailDialogVisible"
|
width="600px"
|
>
|
<div v-if="currentTrip" class="trip-details">
|
<el-descriptions :column="2" border>
|
<el-descriptions-item label="出差单号">
|
{{ currentTrip.tripNumber }}
|
</el-descriptions-item>
|
<el-descriptions-item label="状态">
|
<el-tag :type="getStatusType(currentTrip.status)">
|
{{ currentTrip.status }}
|
</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="出发城市">
|
{{ currentTrip.startCity }}
|
</el-descriptions-item>
|
<el-descriptions-item label="目的城市">
|
{{ currentTrip.endCity }}
|
</el-descriptions-item>
|
<el-descriptions-item label="开始日期">
|
{{ currentTrip.startDate }}
|
</el-descriptions-item>
|
<el-descriptions-item label="结束日期">
|
{{ currentTrip.endDate }}
|
</el-descriptions-item>
|
<el-descriptions-item label="出差天数">
|
{{ currentTrip.duration }}天
|
</el-descriptions-item>
|
<el-descriptions-item label="里程距离">
|
{{ currentTrip.distance }}公里
|
</el-descriptions-item>
|
<el-descriptions-item label="出差目的" :span="2">
|
{{ currentTrip.purpose }}
|
</el-descriptions-item>
|
<el-descriptions-item label="备注" :span="2">
|
{{ currentTrip.remarks || "无" }}
|
</el-descriptions-item>
|
</el-descriptions>
|
|
<div
|
v-if="currentTrip.expenses && currentTrip.expenses.length > 0"
|
class="expenses-section"
|
>
|
<h5>费用明细</h5>
|
<el-table :data="currentTrip.expenses" size="small">
|
<el-table-column prop="item" label="费用项目" />
|
<el-table-column prop="amount" label="金额" />
|
<el-table-column prop="date" label="日期" />
|
</el-table>
|
</div>
|
</div>
|
</el-dialog>
|
|
<!-- 新增/编辑出差对话框 -->
|
<el-dialog
|
:title="isEditing ? '编辑出差记录' : '新增出差记录'"
|
:visible.sync="editDialogVisible"
|
width="500px"
|
>
|
<el-form
|
:model="tripForm"
|
:rules="tripRules"
|
ref="tripForm"
|
label-width="100px"
|
>
|
<el-form-item label="目的城市" prop="endCity">
|
<el-input v-model="tripForm.endCity" placeholder="请输入目的城市" />
|
</el-form-item>
|
<el-form-item label="开始日期" prop="startDate">
|
<el-date-picker
|
v-model="tripForm.startDate"
|
type="date"
|
placeholder="选择开始日期"
|
value-format="yyyy-MM-dd"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
<el-form-item label="结束日期" prop="endDate">
|
<el-date-picker
|
v-model="tripForm.endDate"
|
type="date"
|
placeholder="选择结束日期"
|
value-format="yyyy-MM-dd"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
<el-form-item label="出差目的" prop="purpose">
|
<el-input
|
v-model="tripForm.purpose"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入出差目的"
|
/>
|
</el-form-item>
|
</el-form>
|
<span slot="footer">
|
<el-button @click="editDialogVisible = false">取消</el-button>
|
<el-button type="primary" @click="saveTrip">保存</el-button>
|
</span>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
export default {
|
name: "PersonBusiness",
|
props: {
|
data: {
|
type: Array,
|
default: () => []
|
},
|
loading: {
|
type: Boolean,
|
default: false
|
}
|
},
|
data() {
|
return {
|
currentPage: 1,
|
pageSize: 10,
|
filterForm: {
|
dateRange: [],
|
destination: "",
|
status: ""
|
},
|
detailDialogVisible: false,
|
editDialogVisible: false,
|
isEditing: false,
|
currentTrip: null,
|
tripForm: {
|
endCity: "",
|
startDate: "",
|
endDate: "",
|
purpose: ""
|
},
|
tripRules: {
|
endCity: [
|
{ required: true, message: "请输入目的城市", trigger: "blur" }
|
],
|
startDate: [
|
{ required: true, message: "请选择开始日期", trigger: "change" }
|
],
|
endDate: [
|
{ required: true, message: "请选择结束日期", trigger: "change" }
|
],
|
purpose: [
|
{ required: true, message: "请输入出差目的", trigger: "blur" }
|
]
|
}
|
};
|
},
|
computed: {
|
totalRecords() {
|
return this.filteredTrips.length;
|
},
|
filteredTrips() {
|
let filtered = this.data;
|
|
// 按日期范围过滤
|
if (this.filterForm.dateRange && this.filterForm.dateRange.length === 2) {
|
const [start, end] = this.filterForm.dateRange;
|
filtered = filtered.filter(item => {
|
const itemStart = new Date(item.startDate);
|
return itemStart >= new Date(start) && itemStart <= new Date(end);
|
});
|
}
|
|
// 按目的地过滤
|
if (this.filterForm.destination) {
|
filtered = filtered.filter(item =>
|
item.endCity.includes(this.filterForm.destination)
|
);
|
}
|
|
// 按状态过滤
|
if (this.filterForm.status) {
|
filtered = filtered.filter(
|
item => item.status === this.filterForm.status
|
);
|
}
|
|
return filtered;
|
}
|
},
|
methods: {
|
getStatusType(status) {
|
const typeMap = {
|
进行中: "primary",
|
已完成: "success",
|
已取消: "danger"
|
};
|
return typeMap[status] || "info";
|
},
|
|
viewTripDetails(trip) {
|
this.currentTrip = trip;
|
this.detailDialogVisible = true;
|
},
|
|
handleAddTrip() {
|
this.isEditing = false;
|
this.tripForm = {
|
endCity: "",
|
startDate: "",
|
endDate: "",
|
purpose: ""
|
};
|
this.editDialogVisible = true;
|
},
|
|
editTrip(trip) {
|
this.isEditing = true;
|
this.currentTrip = trip;
|
this.tripForm = { ...trip };
|
this.editDialogVisible = true;
|
},
|
|
saveTrip() {
|
this.$refs.tripForm.validate(valid => {
|
if (valid) {
|
// 这里应该调用API保存数据
|
this.$message.success(this.isEditing ? "修改成功" : "新增成功");
|
this.editDialogVisible = false;
|
this.$emit("refresh");
|
}
|
});
|
},
|
|
handleFilter() {
|
this.currentPage = 1;
|
},
|
|
handleReset() {
|
this.filterForm = {
|
dateRange: [],
|
destination: "",
|
status: ""
|
};
|
this.currentPage = 1;
|
},
|
|
exportToExcel() {
|
// 简化版导出逻辑
|
this.$message.success("导出功能开发中");
|
},
|
|
handleSizeChange(size) {
|
this.pageSize = size;
|
this.currentPage = 1;
|
},
|
|
handleCurrentChange(page) {
|
this.currentPage = page;
|
}
|
}
|
};
|
</script>
|
|
<style scoped>
|
.personal-business-trip-table {
|
padding: 20px;
|
}
|
|
.table-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
}
|
|
.table-header h4 {
|
margin: 0;
|
color: #303133;
|
font-size: 16px;
|
}
|
|
.header-actions {
|
display: flex;
|
gap: 10px;
|
}
|
|
.filter-card {
|
margin-bottom: 20px;
|
}
|
|
.business-trip-table {
|
margin-bottom: 20px;
|
}
|
|
.city-cell {
|
font-weight: 500;
|
}
|
|
.date-cell {
|
color: #606266;
|
}
|
|
.distance-cell {
|
font-weight: 600;
|
color: #409eff;
|
}
|
|
.purpose-cell {
|
color: #606266;
|
font-size: 13px;
|
}
|
|
.pagination-container {
|
display: flex;
|
justify-content: flex-end;
|
margin-top: 20px;
|
}
|
|
.trip-details {
|
padding: 10px;
|
}
|
|
.expenses-section {
|
margin-top: 20px;
|
}
|
|
.expenses-section h5 {
|
margin-bottom: 10px;
|
color: #303133;
|
}
|
|
/* 响应式设计 */
|
@media (max-width: 768px) {
|
.table-header {
|
flex-direction: column;
|
align-items: flex-start;
|
gap: 10px;
|
}
|
|
.header-actions {
|
width: 100%;
|
justify-content: flex-end;
|
}
|
}
|
</style>
|