添加了html2pdf库,以及结算单管理接入接口

This commit is contained in:
hr121 2025-07-31 19:07:10 +08:00
parent a69c3d1908
commit 79bd6b57dd
10 changed files with 746 additions and 196 deletions

View File

@ -44,6 +44,7 @@
"element-ui": "^2.15.14",
"file-saver": "1.3.8",
"fuse.js": "3.4.4",
"html2pdf.js": "^0.10.3",
"js-beautify": "^1.10.2",
"js-cookie": "2.2.0",
"jsencrypt": "^3.0.0-rc.1",

View File

@ -9,4 +9,11 @@ export function getScenicValue (data) {
})
}
export default { getScenicValue }
// 获取用户名称下拉
export function getUserNameValue (userType) {
return request({
url: `api/dataDropdown/obtainUserListByType/${userType}`,
method: 'get',
})
}
export default { getScenicValue, getUserNameValue }

View File

@ -44,7 +44,13 @@ export function generateSettlementOrder(params) {
}
// 获取生成结算单确认列表
// export function get
export function getGenerateSettleOrderConfirmList(params) {
return request({
url: '/api/settlementOrder/generateSettlementOrderConfirmList',
method: 'post',
data: params
})
}
// 更新结算单状态
export function updateSettleOrderStatus(settlementOrderId, settlementOrderStatus) {
@ -53,3 +59,11 @@ export function updateSettleOrderStatus(settlementOrderId, settlementOrderStatus
method: 'put',
})
}
// 打印结算单数据
export function printSettleOrderData(settlementOrderId) {
return request({
url: `api/settlementOrder/printSettlementOrder/${settlementOrderId}`,
method: 'get',
})
}

View File

@ -39,4 +39,12 @@ export function allCustomer() {
})
}
export default { add, enable, del, edit, allCustomer }
// 查询单个客户信息
export function getCustomerId(id) {
return request({
url: `aerocraftAdminApi/cnCustomer/${id}`,
method: 'get'
})
}
export default { add, enable, del, edit, allCustomer, getCustomerId }

View File

@ -517,7 +517,7 @@ export default {
if (response) {
// total
if (response.total !== undefined) {
this.page.total = response.total;
this.page.total = Number(response.total);
}
// records
if (response && Array.isArray(response)) {

View File

@ -144,7 +144,7 @@ export default {
this.getScenics();
this.getPilots();
this.getCustomers();
this.getList();
// this.getList();
},
data() {
return {
@ -207,7 +207,7 @@ export default {
id: record.id,
};
});
this.page.total = response.total || 0;
this.page.total = Number(response.total) || 0;
})
.finally(() => {
this.loading = false;

View File

@ -8,12 +8,12 @@
<div class="filter-container">
<div class="filter-item">
<span class="label">景区</span>
<el-select v-model="query.scenicArea" placeholder="请选择景区" clearable style="width: 200px;">
<el-select v-model="query.attractionId" placeholder="请选择景区" clearable style="width: 200px;">
<el-option
v-for="item in scenicAreaOptions"
:key="item.value"
:label="item.label"
:value="item.value"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</div>
@ -56,19 +56,29 @@
<el-table-column prop="customerName" label="客户名称" align="center" />
<el-table-column prop="phoneNumber" label="手机号码" align="center" />
<el-table-column prop="orderTime" label="下单时间" align="center" width="180" />
<el-table-column prop="orderAmount" label="下单费用" align="center" />
<el-table-column label="下单费用" width="180" align="center">
<template slot-scope="scope">
<div>基础费用{{ scope.row.orderAmount.toFixed(2) }}</div>
<div>附加费用{{ scope.row.surchargeAmount.toFixed(4) }}</div>
<div>总费用{{ scope.row.totalAmount.toFixed(4) }}</div>
</template>
</el-table-column>
<el-table-column prop="scenicArea" label="景区" align="center" />
<el-table-column prop="route" label="路线" align="center" />
<el-table-column prop="initiator" label="订单发起人" align="center" />
<el-table-column prop="orderType" label="订单类型" align="center" />
<el-table-column prop="orderStatus" label="订单状态" align="center">
<template slot-scope="scope">
<span>{{ scope.row.orderStatus }}</span>
<span :class="['status-text', scope.row.orderStatus === '已完成' ? 'success' : '']">
{{ scope.row.orderStatus }}
</span>
</template>
</el-table-column>
<el-table-column prop="settlementStatus" label="结算状态" align="center">
<template slot-scope="scope">
<span>{{ scope.row.settlementStatus }}</span>
<span :class="['status-text', scope.row.settlementStatus === '已结算' ? 'success' : '']">
{{ scope.row.settlementStatus }}
</span>
</template>
</el-table-column>
</el-table>
@ -123,9 +133,26 @@
</div>
</template>
<!--
先调用获取生成结算单确认列表接口,当点击确认生成时调用生成结算单接口
-->
<script>
import { getOrderList, getGenerateSettleOrderConfirmList, generateSettlementOrder } from "@/api/order";
import { allAreas } from "@/api/system/area";
import { allScenic } from "@/api/system/scenic";
import { allCustomer } from "@/api/system/customer";
import { getList } from "@/api/system/pilot";
import { getUser } from "@/api/system/user";
import store from "@/store";
export default {
name: 'generateOrder',
created() {
this.getScenics();
this.getCustomers();
this.getPilots();
this.getList();
},
data() {
const defaultStartDate = new Date('2015-10-02')
const defaultEndDate = new Date('2015-10-10')
@ -134,7 +161,8 @@ export default {
settlementList: [],
loading: false,
query: {
scenicArea: '',
attractionId: undefined,
customerId: undefined,
dateRange: [defaultStartDate, defaultEndDate]
},
defaultDateRange: [defaultStartDate, defaultEndDate],
@ -143,36 +171,134 @@ export default {
size: 10,
total: 0
},
scenicAreaOptions: [
{ value: 'baiYunShan', label: '白云山' },
{ value: 'huangShan', label: '黄山' },
{ value: 'taiShan', label: '泰山' }
],
tableData: [
{
customerName: '小明',
phoneNumber: '12345678',
orderTime: '2024-01-01 00:00:00',
orderAmount: '100.00',
scenicArea: '白云山',
route: 'a-b',
initiator: '小红',
orderType: '载物飞行',
orderStatus: '已完成',
settlementStatus: '未结算'
}
],
scenicAreaOptions: [],
customerOptions: [],
pilotOptions: [],
tableData: [],
selection: []
}
},
methods: {
getList() {
this.loading = true;
const params = {
attractionId: this.query.attractionId,
customerId: this.query.customerId,
startTime: this.query.dateRange ? this.query.dateRange[0] : null,
endTime: this.query.dateRange ? this.query.dateRange[1] : null,
current: this.page.current,
size: this.page.size,
mainOrderStatus: 2, //
settlementStatus: 0 //
};
getOrderList(params)
.then((response) => {
this.tableData = response.records.map((record) => {
const customer = this.customerOptions.find(
(c) => c.id === record.customerId
);
const scenic = this.scenicAreaOptions.find(s => s.id === record.attractionId);
const initiator = this.pilotOptions.find(p => p.id === record.orderInitiatorId);
return {
id: record.id,
customerName: customer ? customer.name : "未知客户",
phoneNumber: record.customerPhone,
orderTime: record.orderCreateTime,
orderAmount: record.amount,
totalAmount: record.totalAmount,
surchargeAmount: record.surchargeAmount,
scenicArea: scenic ? scenic.name : "未知景区",
route: record.routeName,
initiator: initiator ? initiator.name : "未知发起人",
orderType: record.orderType === 1 ? "载物飞行" : "载人飞行",
orderStatus: this.getOrderStatus(record.mainOrderStatus),
settlementStatus: this.getSettlementStatus(record.settlementStatus),
};
});
this.page.total = Number(response.total) || 0;
})
.finally(() => {
this.loading = false;
});
},
async getScenics() {
try {
let res = [];
const userRes = await getUser({ id: store.getters.user.id });
if (userRes && userRes.content && userRes.content.length > 0) {
const userInfo = userRes.content[0];
if (userInfo.roles && userInfo.roles.length > 0) {
this.userRole = userInfo.roles[0].name;
if (this.userRole === "区域负责人") {
res = await allScenic({areaId: userInfo.areaId});
} else if (this.userRole === "管理员" || this.userRole === "财务") {
res = await allScenic();
}
}
}
if (res && Array.isArray(res)) {
this.scenicAreaOptions = res.map((scenic) => ({
id: scenic.id,
name: scenic.name
}));
}
this.getList();
} catch (error) {
console.error("获取景区列表失败:", error);
}
},
async getPilots() {
try {
const res = await getList();
if (res) {
this.pilotOptions = res.content.map((pilot) => ({
id: pilot.id,
name: pilot.name,
}));
}
} catch (error) {
console.error("获取飞行员列表失败:", error);
}
},
async getCustomers() {
try {
const res = await allCustomer();
if (res) {
this.customerOptions = res.map((customer) => ({
id: customer.id,
name: customer.name,
}));
}
} catch (error) {
console.error("获取客户列表失败:", error);
}
},
getOrderStatus(status) {
const statusMap = {
0: "未进行",
1: "进行中",
2: "已完成",
3: "已取消"
};
return statusMap[status] || "未知状态";
},
getSettlementStatus(status) {
const statusMap = {
0: "未结算",
1: "结算中",
2: "结算完成"
};
return statusMap[status] || "未知状态";
},
handleSearch() {
//
this.loading = true
// API
setTimeout(() => {
this.loading = false
}, 500)
this.page.current = 1;
this.getList();
},
handleSelectAll() {
@ -182,14 +308,12 @@ export default {
this.selection = val
},
handleSizeChange(val) {
this.page.size = val
this.page.total = this.tableData.length //
//
this.page.size = val;
this.getList();
},
handleCurrentChange(val) {
this.page.current = val
this.page.total = this.tableData.length //
//
this.page.current = val;
this.getList();
},
handleDetail(row) {
//
@ -221,14 +345,46 @@ export default {
amount: '100元' //
}))
//
getGenerateSettleOrderConfirmList({
orders: this.selection.map(order => order.id)
})
.then(response => {
if (response.success) {
//
this.settlementList = response.data || []
} else {
this.$message.error('获取结算确认列表失败: ' + response.message)
}
})
.catch(error => {
console.error('获取结算确认列表失败:', error)
this.$message.error('获取结算确认列表失败,请稍后再试')
})
//
this.dialogVisible = true
},
handleConfirm() {
//
generateSettlementOrder({
orders: this.selection.map(order => order.id)
})
.then(response => {
if (response.success) {
this.$message.success('结算单生成成功')
this.dialogVisible = false
this.$router.push('/order/orderDetail')
} else {
this.$message.error('结算单生成失败: ' + response.message)
}
})
.catch(error => {
console.error('生成结算单失败:', error)
this.$message.error('生成结算单失败,请稍后再试')
})
this.dialogVisible = false
//
this.$router.push('/order/orderDetail')
},
handleCancel() {
@ -239,6 +395,13 @@ export default {
</script>
<style lang="scss" scoped>
.status-text {
color: #f56c6c;
&.success {
color: #67c23a;
}
}
.app-container {
padding: 20px;
}

View File

@ -28,16 +28,11 @@
style="width: 100%"
@selection-change="handleSelectionChange"
>
<el-table-column prop="batchNo" label="订单批次号" align="left" />
<el-table-column prop="status" label="结算状态" align="left">
<template slot-scope="scope">
<span>{{ scope.row.status }}</span>
</template>
</el-table-column>
<el-table-column prop="settlementTime" label="结算时间" width="180" align="left" />
<el-table-column prop="operator" label="制单人" align="left" />
<el-table-column prop="exportTime" label="导出时间" width="180" align="left" />
<el-table-column prop="settlementStatus" label="结算状态" align="left">
<el-table-column prop="batchNo" label="订单批次号" align="center" />
<el-table-column prop="settlementTimeScope" label="结算时间" width="180" align="center" />
<el-table-column prop="operator" label="制单人" align="center" />
<el-table-column prop="updateTime" label="导出时间" width="180" align="center" />
<el-table-column prop="settlementStatus" label="结算状态" align="center">
<template slot-scope="scope">
<span>{{ scope.row.settlementStatus }}</span>
</template>
@ -78,6 +73,9 @@
</template>
<script>
import { getSettleOrderList } from '@/api/order'
import { getUserNameValue } from '@/api/dropdown'
export default {
name: 'settlementOrder',
data() {
@ -93,26 +91,65 @@ export default {
},
tableData: [
{
batchNo: 'JS123456789',
status: '结算中',
settlementTime: '2025-01-01 00:00:00',
operator: '小明',
exportTime: '2025-01-01 00:00:00',
settlementStatus: '结算中'
attractionId: 2,
batchNo: "JS_2025072600008",
createBy: "admin",
createTime: "2025-07-26 14:42:38",
operatorId: 1,
settlementStatus: 2,
settlementTimeScope: "2025-07-19 17:00:00~2025-07-19 17:00:00",
updateBy: "admin",
updateTime: "2025-07-26 14:42:38"
},
{
batchNo: 'JS123456789',
status: '待付款',
settlementTime: '2025-01-01 00:00:00',
operator: '小明',
exportTime: '2025-01-01 00:00:00',
settlementStatus: '已确认'
}
],
selection: []
}
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
this.loading = true
try {
const response = await getSettleOrderList(this.query)
const operatorList = await getUserNameValue(0); //
const operatorMap = new Map();
operatorList.forEach(item => {
operatorMap.set(item.key, item.value);
});
response.records = response.records.map(record => ({
...record,
settlementStatus: this.getSettlementStatus(record.settlementStatus),
operator: record.operatorId ? (operatorMap.get(record.operatorId) || '未知') : '未知',
}))
console.log('获取结算单列表:', response)
this.tableData = response.records || []
this.page.total = Number(response.total) || 0
} catch (error) {
console.error('获取结算单列表失败:', error)
} finally {
this.loading = false
}
},
getSettlementStatus(status) {
const statusMap = {
0: "结算中",
1: "已确认",
2: "结算完成",
3: "已取消",
};
return statusMap[status] || "未知状态";
},
async getOperatorName(operatorId) {
const response = await getUserNameValue(0);
if (!response || response.length === 0) {
return '未知';
}
const operatorName = response.find(item => item.key === operatorId);
return operatorName.value || '未知';
},
handleSearch() {
//
console.log('搜索条件:', this.query)
@ -131,6 +168,11 @@ export default {
handleDetail(row) {
//
console.log('查看详情:', row)
//
this.$router.push({
path: '/order/orderDetail',
query: { orderId: row.id.toString() }
})
},
handlePrint(row) {
//
@ -151,7 +193,6 @@ export default {
.filter-container {
display: flex;
align-items: center;
margin-bottom: 20px;
gap: 10px;
.filter-item {

View File

@ -13,6 +13,7 @@
placeholder="请输入订单批次号"
style="width: 200px"
clearable
@input="handleSearch"
/>
</div>
<div class="filter-item">
@ -22,6 +23,7 @@
placeholder="请选择景区"
clearable
style="width: 200px"
@change="handleSearch"
>
<el-option
v-for="item in scenicAreaOptions"
@ -42,6 +44,7 @@
:default-time="['00:00:00', '23:59:59']"
:default-value="defaultDateRange"
style="width: 350px"
@change="handleSearch"
/>
</div>
</div>
@ -57,25 +60,38 @@
<el-table
ref="table"
v-loading="loading"
:data="tableData"
:data="paginatedData"
style="width: 100%"
>
<el-table-column prop="customerName" label="客户名称" align="center" />
<el-table-column prop="phoneNumber" label="手机号码" align="center" />
<el-table-column
prop="orderTime"
label="下单时间"
align="center"
width="180"
/>
<el-table-column prop="orderAmount" label="下单费用" align="center" />
<el-table-column prop="scenicArea" label="景区" align="center" />
<el-table-column prop="route" label="路线" align="center" />
<el-table-column prop="initiator" label="订单发起人" align="center" />
<el-table-column prop="orderType" label="订单类型" align="center" />
<el-table-column prop="orderStatus" label="订单状态" align="center">
<el-table-column prop="phone" label="手机号码" align="center" />
<el-table-column prop="createTime" label="下单时间" align="center" width="180" />
<el-table-column prop="totalAmount" label="下单费用" align="center">
<template slot-scope="scope">
<span>{{ scope.row.orderStatus }}</span>
<span>{{ scope.row.totalAmount.toLocaleString('zh', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</span>
</template>
</el-table-column>
<el-table-column prop="scenicName" label="景区" align="center" />
<el-table-column prop="routeIds" label="路线" align="center" />
<el-table-column prop="createBy" label="订单发起人" align="center" />
<el-table-column
prop="orderType"
label="订单类型"
align="center"
>
<template slot-scope="scope">
<span>{{ scope.row.orderType === 1 ? '载物飞行' : '载人飞行' }}</span>
</template>
</el-table-column>
<el-table-column
prop="mainOrderStatus"
label="订单状态"
align="center"
>
<template slot-scope="scope">
<span :class="getSettlementStatusClass(scope.row.mainOrderStatus)">
{{ getOrderStatus(scope.row.mainOrderStatus) }}
</span>
</template>
</el-table-column>
<el-table-column
@ -85,7 +101,7 @@
>
<template slot-scope="scope">
<span :class="getSettlementStatusClass(scope.row.settlementStatus)">
{{ scope.row.settlementStatus }}
{{ getSettlementStatusText(scope.row.settlementStatus) }}
</span>
</template>
</el-table-column>
@ -96,7 +112,7 @@
:current-page.sync="page.current"
:page-sizes="[10, 20, 50, 100]"
:page-size="page.size"
:total="page.total"
:total="tableData.length"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@ -107,31 +123,58 @@
<!-- 操作按钮 -->
<div class="action-buttons">
<el-button
v-if="settlementStatus === 0"
type="primary"
@click="handleCancelSettlement"
:disabled="settlementStatus !== '结算中'"
>
取消结算
</el-button>
<el-button type="primary" @click="handlePrint"> 打印结算单 </el-button>
<el-button type="primary" @click="handlePrint">导出结算单</el-button>
<el-button
v-if="settlementStatus === 0"
type="success"
@click="handleStatusChange(1)"
>
确认结算
</el-button>
<el-button
v-if="settlementStatus === 1"
type="success"
@click="handleStatusChange(2)"
>
完成结算
</el-button>
</div>
<!-- PDF导出组件 -->
<settlement-pdf
ref="settlementPdf"
:order-data="currentOrder"
v-show="false"
/>
</div>
</template>
<script>
import { getSettleOrderDetail, updateSettleOrderStatus } from '@/api/order'
import { getDetail } from "@/api/system/pilot";
import { getCustomerId } from '@/api/system/customer'
import { getScenicValue } from '@/api/dropdown'
import SettlementPdf from './settlementPdf.vue'
export default {
name: "orderDetail",
components: {
SettlementPdf
},
data() {
const defaultStartDate = new Date("2015-10-02");
const defaultEndDate = new Date("2015-10-10");
const defaultStartDate = new Date();
const defaultEndDate = new Date();
return {
loading: false,
settlementStatus: "结算中", //
scenicAreaOptions: [
{ value: "baiYunShan", label: "白云山" },
{ value: "huangShan", label: "黄山" },
{ value: "taiShan", label: "泰山" },
],
scenicAreaOptions: [],
settlementStatus: 0,
currentOrder: null,
query: {
batchNo: "",
scenicArea: "",
@ -140,98 +183,152 @@ export default {
defaultDateRange: [defaultStartDate, defaultEndDate],
page: {
current: 1,
size: 10,
total: 3,
size: 10
},
tableData: [
{
customerName: "小明",
phoneNumber: "12345678",
orderTime: "2024-01-01 00:00:00",
orderAmount: "100.00",
scenicArea: "白云山",
route: "a-b",
initiator: "小红",
orderType: "载物飞行",
orderStatus: "已完成",
settlementStatus: "未结算",
},
{
customerName: "小明",
phoneNumber: "12345678",
orderTime: "2024-01-01 00:00:00",
orderAmount: "100.00",
scenicArea: "白云山",
route: "a-b",
initiator: "小红",
orderType: "载物飞行",
orderStatus: "进行中",
settlementStatus: "结算中",
},
{
customerName: "小明",
phoneNumber: "12345678",
orderTime: "2024-01-01 00:00:00",
orderAmount: "100.00",
scenicArea: "白云山",
route: "a-b",
initiator: "小红",
orderType: "载物飞行",
orderStatus: "已完成",
settlementStatus: "结算完成",
},
],
tableData: [],
selection: [],
};
},
computed: {
paginatedData() {
const start = (this.page.current - 1) * this.page.size
const end = start + this.page.size
return this.tableData.slice(start, end)
}
},
async created() {
//
const scenicResponse = await getScenicValue()
this.scenicAreaOptions = scenicResponse.map(item => ({
value: item.key,
label: item.value
}))
//
const orderId = this.$route.query.orderId
if (orderId) {
this.loading = true
try {
const settleOrderDetail = await getSettleOrderDetail(orderId)
await Promise.all(settleOrderDetail.map(async (item) => {
//
const customerResponse = await getCustomerId(item.customerId)
item.customerName = customerResponse.name
//
const pilotResponse = await getDetail(item.orderInitiatorId)
item.createBy = pilotResponse.name
//
const scenic = scenicResponse.find(s => s.key === item.attractionId)
item.scenicName = scenic ? scenic.value : ''
}))
this.tableData = settleOrderDetail
if(settleOrderDetail.length > 0) {
this.currentOrder = settleOrderDetail[0]
this.settlementStatus = settleOrderDetail[0].settlementStatus
}
} catch (error) {
console.error('Error fetching order details:', error)
this.$message.error('获取订单详情失败')
} finally {
this.loading = false
}
}
},
methods: {
handleSearch() {
//
console.log("搜索条件:", this.query);
async handleSearch() {
this.loading = true
try {
const params = {
batchNo: this.query.batchNo,
attractionId: this.query.scenicArea,
startTime: this.query.dateRange ? this.query.dateRange[0] : null,
endTime: this.query.dateRange ? this.query.dateRange[1] : null
}
const settleOrderDetail = await getSettleOrderDetail(params)
await Promise.all(settleOrderDetail.map(async (item) => {
const customerResponse = await getCustomerId(item.customerId)
item.customerName = customerResponse.name
const scenic = this.scenicAreaOptions.find(s => s.value === item.attractionId)
item.scenicName = scenic ? scenic.label : ''
}))
this.tableData = settleOrderDetail
//
this.page.current = 1
} catch (error) {
console.error('Error searching orders:', error)
this.$message.error('搜索订单失败')
} finally {
this.loading = false
}
},
getOrderStatus(status) {
const statusMap = {
0: "未进行",
1: "进行中",
2: "已完成",
3: "已取消"
};
return statusMap[status] || "未知状态";
},
getSettlementStatusText(status) {
const statusMap = {
0: '结算中',
1: '已确认',
2: '结算完成',
3: '已取消'
};
return statusMap[status] || '未知状态';
},
getSettlementStatusClass(status) {
return {
"status-red": ["未结算", "结算中"].includes(status),
'status-red': [0, 3].includes(status),
'status-green': status === 2,
'status-orange': status === 1
};
},
handleCancelSettlement() {
if (this.settlementStatus === "结算中") {
//
this.tableData.forEach((order) => {
order.settlementStatus = "未结算";
});
this.settlementStatus = "未结算";
this.$message.success("已取消结算");
async handleCancelSettlement() {
try {
await updateSettleOrderStatus(this.$route.query.orderId, 3)
this.settlementStatus = 3
this.$message.success('取消结算成功')
this.handleSearch() //
} catch (error) {
console.error('Error canceling settlement:', error)
this.$message.error('取消结算失败')
}
},
handleSizeChange(val) {
this.page.size = val;
//
this.page.current = 1; //
},
handleCurrentChange(val) {
this.page.current = val;
//
},
handleDetail(row) {
//
console.log("查看详情:", row);
async handlePrint() {
// PDF
if (this.currentOrder) {
try {
await this.$refs.settlementPdf.generatePDF()
} catch (error) {
console.error('Error generating PDF:', error)
this.$message.error('导出PDF失败')
}
} else {
this.$message.warning('没有可导出的订单数据')
}
},
handlePrint() {
//
switch (this.settlementStatus) {
case "结算中":
this.settlementStatus = "已确认";
this.$message.success("结算单状态已更新为:已确认");
break;
case "已确认":
this.settlementStatus = "已完成";
this.$message.success("结算单状态已更新为:已完成");
break;
case "已完成":
//
console.log("打印结算单");
break;
async handleStatusChange(status) {
try {
await updateSettleOrderStatus(this.$route.query.orderId, status)
this.settlementStatus = status
const statusText = status === 1 ? '已确认' : '已完成'
this.$message.success(`结算单状态已更新为:${statusText}`)
this.handleSearch() //
} catch (error) {
console.error('Error updating settlement status:', error)
this.$message.error('更新结算状态失败')
}
}
}
@ -293,26 +390,19 @@ export default {
.status-red {
color: #f56c6c;
}
// .pagination-container {
// position: fixed;
// bottom: 0;
// left: 200px;
// right: 0;
// height: 60px;
// background: white;
// padding: 10px;
// text-align: left;
// box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.08);
// z-index: 1000;
// }
// .el-pagination {
// padding: 10px 20px;
// }
.status-green {
color: #67c23a;
}
.status-orange {
color: #e6a23c;
}
.el-table {
margin-bottom: 70px;
margin-top: 20px;
::v-deep .el-table__body-wrapper {
overflow-x: auto;
}
}
.el-button--text {
@ -321,11 +411,4 @@ export default {
color: #66b1ff;
}
}
.el-table {
margin-top: 20px;
::v-deep .el-table__body-wrapper {
overflow-x: auto;
}
}
</style>

View File

@ -0,0 +1,233 @@
<template>
<div>
<!-- PDF 可见区域 -->
<div id="pdf-content" ref="pdfContent" style="width: 100%; margin: 0 auto; padding: 20px;">
<h2 class="pdf-title">爱尚云收款结算单</h2>
<div class="info-row">
<div class="info-item">
<span class="label">结算单号:</span>
<span class="value">{{ data.statementId }}</span>
</div>
<div class="info-item">
<span class="label">结算时间:</span>
<span class="value">{{ data.date }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="label">委托方:</span>
<span class="value">{{ data.customer }}</span>
</div>
<div class="info-item">
<span class="label">数量:</span>
<span class="value">{{ data.quantity }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="label">金额RMB:</span>
<span class="value">{{ data.amount }}</span>
</div>
<div class="info-item">
<span class="label">大写</span>
<span class="value">{{ data.amountCN }}</span>
</div>
</div>
<table border="1" cellspacing="0" cellpadding="5" width="100%">
<thead>
<tr>
<th style="width: 20%;">订单号</th>
<th style="width: 25%;">执行时间</th>
<th style="width: 15%;">重量</th>
<th style="width: 20%;">金额</th>
<th style="width: 20%;">确认人</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in data.items" :key="index">
<td>{{ item.orderNo }}</td>
<td>{{ item.execTime }}</td>
<td>{{ item.weight }}</td>
<td>{{ item.amount }}</td>
<td>{{ item.confirmer }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import html2pdf from 'html2pdf.js'
export default {
name: 'SettlementPdf',
props: {
orderData: {
type: Object,
default: () => ({})
}
},
data() {
return {
data: {
statementId: '',
date: '',
customer: '',
quantity: '',
amount: '',
amountCN: '',
items: []
}
}
},
watch: {
orderData: {
handler(newVal) {
if (newVal) {
this.formatData(newVal)
}
},
immediate: true
}
},
methods: {
formatData(orderData) {
if (!orderData) {
console.warn('No order data provided')
return
}
console.log('Formatting order data:', orderData)
// PDF
this.data = {
statementId: orderData.orderNo || '',
date: orderData.createTime || '',
customer: orderData.customerName || '',
quantity: orderData.cargoWeight ? `${orderData.cargoWeight}KG` : '0KG',
amount: orderData.totalAmount ? `${orderData.totalAmount}` : '0元',
amountCN: this.convertToChinese(orderData.totalAmount || 0),
items: [{
orderNo: orderData.orderType === 1?'载物飞行':'载人飞行' || orderData.orderNo || '',
execTime: orderData.orderFinishTime || '',
weight: orderData.cargoWeight ? `${orderData.cargoWeight}KG` : '0KG',
amount: orderData.totalAmount ? `${orderData.totalAmount}` : '0元',
confirmer: orderData.createBy || ''
}]
}
},
generatePDF() {
const element = this.$refs.pdfContent
const opt = {
margin: 0.5,
filename: `${this.data.statementId}.pdf`,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'in', format: 'a4', orientation: 'portrait' }
}
return html2pdf().set(opt).from(element).save()
},
convertToChinese(num) {
const digitCN = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
const unitCN = ['', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿']
let str = ''
//
const intNum = Math.floor(num)
const intStr = intNum.toString()
for (let i = 0; i < intStr.length; i++) {
const digit = parseInt(intStr[i])
str += digitCN[digit] + unitCN[intStr.length - 1 - i]
}
//
const decimalNum = Math.round((num - intNum) * 100)
if (decimalNum > 0) {
str += '元'
const decStr = decimalNum.toString().padStart(2, '0')
if (decStr[0] !== '0') {
str += digitCN[parseInt(decStr[0])] + '角'
}
if (decStr[1] !== '0') {
str += digitCN[parseInt(decStr[1])] + '分'
}
} else {
str += '元整'
}
return str
}
}
}
</script>
<style scoped>
.pdf-title {
text-align: center;
font-size: 24px;
font-weight: bold;
margin-bottom: 20px;
font-family: SimSun, "宋体", serif;
}
.info-row {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
margin-bottom: 5px;
line-height: 1.8;
font-size: 20px;
font-family: SimSun, "宋体", serif;
}
.info-item {
flex: 0 0 45%;
margin-bottom: 10px;
/* display: flex;
flex-wrap: wrap; */
word-break: break-word;
}
.info-item .label {
white-space: nowrap;
margin-right: 5px;
}
.info-item .value {
box-sizing: border-box;
/* 增加下划线与字体距离 */
text-decoration: underline;
text-underline-offset: 16px;
word-break: break-word;
}
table {
border-collapse: collapse;
margin-top: 30px;
font-size: 16px;
}
th, td {
text-align: center;
padding: 12px 8px;
word-break: break-word;
white-space: normal;
line-height: 1.6;
}
/* 执行时间列的样式 */
th:nth-child(2),
td:nth-child(2) {
font-family: Calibri, sans-serif;
}
/* 重量列的样式 */
td:nth-child(3) {
font-weight: bold;
}
#pdf-content {
line-height: 1.6;
}
</style>