<template>
|
<view class="invoice-apply-container bg-white">
|
<form>
|
<view class="cu-form-group margin-top">
|
<view class="title required">选择任务</view>
|
<view class="combo-box-wrapper">
|
<input
|
class="combo-input"
|
placeholder="请输入服务单号或任务编号搜索"
|
v-model="searchKeyword"
|
@input="handleInput"
|
@focus="handleFocus"
|
@blur="handleBlur"
|
/>
|
<text class="cuIcon-search search-icon"></text>
|
|
<!-- 下拉列表 -->
|
<view class="dropdown-list" v-if="showDropdown && filteredTaskList.length > 0">
|
<view class="dropdown-item"
|
v-for="task in filteredTaskList"
|
:key="task.taskId"
|
@click="selectTask(task)"
|
>
|
<view class="item-main">
|
<text class="service-code">{{ task.serviceCode || task.taskCode }}</text>
|
<text class="task-code">{{ task.taskCode }}</text>
|
</view>
|
<view class="item-sub">
|
|
<text class="text-gray margin-left-sm">{{ task.completionTime }}</text>
|
</view>
|
</view>
|
</view>
|
|
<!-- 无结果提示 -->
|
<view class="dropdown-list" v-if="showDropdown && searchKeyword && filteredTaskList.length === 0 && !loading">
|
<view class="empty-result">
|
<text class="text-gray">未找到匹配的任务</text>
|
</view>
|
</view>
|
|
<!-- 加载提示 -->
|
<view class="dropdown-list" v-if="showDropdown && loading">
|
<view class="loading-result">
|
<text class="cuIcon-loading2 cu-spin"></text>
|
<text class="text-gray margin-left-sm">搜索中...</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<!-- 已选任务显示 -->
|
<view class="selected-task" v-if="form.serviceOrderId">
|
<view class="label">已选任务</view>
|
<view class="task-card">
|
<view class="card-header">
|
<text class="service-code">{{ selectedTask.serviceCode || selectedTask.taskCode }}</text>
|
<text class="cuIcon-close remove-btn" @click="clearSelection"></text>
|
</view>
|
<view class="task-info-detail">
|
<view class="info-row">
|
<text class="info-label">服务单号:</text>
|
<text class="info-value">{{ selectedTask.legacyServiceOrderId }}</text>
|
</view>
|
<view class="info-row">
|
<text class="info-label">出发地:</text>
|
<text class="info-value">{{ selectedTask.departure || '-' }}</text>
|
</view>
|
<view class="info-row">
|
<text class="info-label">目的地:</text>
|
<text class="info-value">{{ selectedTask.destination || '-' }}</text>
|
</view>
|
<view class="info-row">
|
<text class="info-label">完成时间:</text>
|
<text class="info-value">{{ selectedTask.completionTime }}</text>
|
</view>
|
<view class="info-row" v-if="selectedTask.transferPrice">
|
<text class="info-label">任务金额:</text>
|
<text class="info-value text-price">¥{{ selectedTask.transferPrice }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<view class="cu-form-group">
|
<view class="title">开票类型</view>
|
<radio-group class="flex" @change="handleTypeChange">
|
<view class="margin-right-sm">
|
<radio value="1" :checked="form.invoiceType == 1"></radio><text class="margin-left-xs">个人</text>
|
</view>
|
<view>
|
<radio value="2" :checked="form.invoiceType == 2"></radio><text class="margin-left-xs">企业</text>
|
</view>
|
</radio-group>
|
</view>
|
|
<view class="cu-form-group">
|
<view class="title required">发票抬头</view>
|
<input placeholder="请输入发票抬头" v-model="form.invoiceName" />
|
</view>
|
|
<view class="cu-form-group">
|
<view class="title required">发票金额</view>
|
<input
|
type="digit"
|
placeholder="请输入金额"
|
v-model="form.invoiceMoney"
|
@blur="validateMoney"
|
/>
|
</view>
|
<view class="money-hint" v-if="selectedTask && selectedTask.transferPrice">
|
<text class="text-gray text-sm">最大可开票金额: ¥{{ selectedTask.transferPrice }}</text>
|
</view>
|
|
<view class="cu-form-group align-start">
|
<view class="title">发票备注</view>
|
<textarea maxlength="-1" v-model="form.invoiceRemarks" placeholder="请输入备注信息"></textarea>
|
</view>
|
|
<block v-if="form.invoiceType == 2">
|
<view class="cu-form-group">
|
<view class="title">注册地址</view>
|
<input placeholder="请输入企业注册地址" v-model="form.companyAddress" />
|
</view>
|
<view class="cu-form-group">
|
<view class="title">开户银行</view>
|
<input placeholder="请输入企业开户银行" v-model="form.companyBank" />
|
</view>
|
<view class="cu-form-group">
|
<view class="title">银行帐号</view>
|
<input placeholder="请输入企业银行帐号" v-model="form.companyBankNo" />
|
</view>
|
</block>
|
|
<view class="cu-form-group margin-top-sm">
|
<view class="title">邮编</view>
|
<input placeholder="请输入邮编" v-model="form.zipCode" />
|
</view>
|
|
<view class="cu-form-group">
|
<view class="title">邮寄地址</view>
|
<input placeholder="请输入邮寄地址" v-model="form.mailAddress" />
|
</view>
|
|
<view class="cu-form-group">
|
<view class="title">联系人</view>
|
<input placeholder="请输入联系人" v-model="form.contactName" />
|
</view>
|
|
<view class="cu-form-group">
|
<view class="title">联系电话</view>
|
<input placeholder="请输入联系电话" v-model="form.contactPhone" />
|
</view>
|
|
<view class="padding flex flex-direction">
|
<button class="cu-btn bg-blue lg" @click="submit">提交申请</button>
|
</view>
|
</form>
|
</view>
|
</template>
|
|
<script>
|
import { addInvoice, listSelectableTasks } from "@/api/invoice"
|
|
export default {
|
data() {
|
return {
|
searchKeyword: '',
|
filteredTaskList: [],
|
selectedTask: null,
|
showDropdown: false,
|
loading: false,
|
searchTimer: null,
|
maxInvoiceMoney: null, // 最大可开票金额
|
form: {
|
serviceOrderId: null,
|
legacyServiceOrderId: null,
|
invoiceType: 1,
|
invoiceName: '',
|
invoiceMoney: '',
|
invoiceRemarks: '',
|
companyAddress: '',
|
companyBank: '',
|
companyBankNo: '',
|
zipCode: '',
|
mailAddress: '',
|
contactName: '',
|
contactPhone: ''
|
},
|
// 从任务详情页传入的任务信息
|
taskInfoFromDetail: null
|
}
|
},
|
onLoad(options) {
|
// 检查是否从任务详情页传入了任务信息
|
if (options && options.taskInfo) {
|
try {
|
const taskInfo = JSON.parse(decodeURIComponent(options.taskInfo));
|
this.taskInfoFromDetail = taskInfo;
|
this.preFillTaskInfo();
|
} catch (e) {
|
console.error('解析任务信息失败:', e);
|
}
|
}
|
// 不再自动加载任务列表
|
},
|
methods: {
|
// 预填充任务信息
|
preFillTaskInfo() {
|
if (!this.taskInfoFromDetail) return;
|
|
const taskInfo = this.taskInfoFromDetail;
|
|
// 填充表单
|
this.form.serviceOrderId = taskInfo.taskId;
|
// 同时设置旧系统服务单ID
|
if (taskInfo.legacyServiceOrderId) {
|
this.form.legacyServiceOrderId = taskInfo.legacyServiceOrderId;
|
}
|
this.selectedTask = { ...taskInfo };
|
|
// 设置最大可开票金额
|
if (taskInfo.transferPrice) {
|
this.maxInvoiceMoney = parseFloat(taskInfo.transferPrice);
|
// 自动填入任务金额
|
this.form.invoiceMoney = this.maxInvoiceMoney.toString();
|
}
|
|
console.log('任务信息已预填充:', taskInfo);
|
},
|
|
handleInput(e) {
|
const keyword = e.detail.value.trim()
|
|
// 清除之前的定时器
|
if (this.searchTimer) {
|
clearTimeout(this.searchTimer)
|
}
|
|
if (!keyword) {
|
this.showDropdown = false
|
this.filteredTaskList = []
|
return
|
}
|
|
// 防抖:延迟300ms搜索
|
this.searchTimer = setTimeout(() => {
|
this.searchTasks(keyword)
|
}, 300)
|
},
|
|
handleFocus() {
|
// 如果有输入内容,显示下拉列表
|
if (this.searchKeyword && this.searchKeyword.trim()) {
|
this.showDropdown = true
|
}
|
},
|
|
handleBlur() {
|
// 延迟关闭下拉列表,确保点击事件能触发
|
setTimeout(() => {
|
this.showDropdown = false
|
}, 200)
|
},
|
|
searchTasks(keyword) {
|
this.loading = true
|
this.showDropdown = true
|
|
listSelectableTasks({
|
searchKeyword: keyword
|
}).then(res => {
|
this.filteredTaskList = this.formatTaskList(res.data)
|
}).catch(err => {
|
this.$modal.msgError('搜索失败,请重试')
|
this.filteredTaskList = []
|
}).finally(() => {
|
this.loading = false
|
})
|
},
|
formatTaskList(data) {
|
return data.map(item => {
|
// 完整显示 yyyy-MM-dd HH:mm
|
const time = item.completionTime ? item.completionTime.substring(0, 16) : '';
|
// 兼容两种字段名:transferPrice 和 transfer_price
|
const transferPrice = item.transferPrice !== undefined ? item.transferPrice : item.transfer_price;
|
return {
|
taskId: item.taskId,
|
taskCode: item.taskCode,
|
serviceCode: item.serviceCode,
|
legacyServiceOrderId: item.legacyServiceOrderId || item.taskCode,
|
completionTime: time,
|
departure: item.departure,
|
destination: item.destination,
|
transferPrice: transferPrice
|
}
|
})
|
},
|
handleSearch() {
|
if (!this.searchKeyword || this.searchKeyword.trim() === '') {
|
// 未输入时显示所有
|
this.getCompletedTasks()
|
} else {
|
// 调用后端搜索接口
|
listSelectableTasks({
|
searchKeyword: this.searchKeyword.trim()
|
}).then(res => {
|
this.filteredTaskList = this.formatTaskList(res.data)
|
})
|
}
|
},
|
selectTask(task) {
|
console.log('选中的任务:', task)
|
console.log('任务金额:', task.transferPrice)
|
|
this.selectedTask = task
|
// serviceOrderId 存储旧系统服务单ID
|
this.form.serviceOrderId = task.taskId;
|
this.form.legacyServiceOrderId = task.legacyServiceOrderId
|
// 选中后显示服务单号,关闭下拉
|
this.searchKeyword = task.serviceCode || task.taskCode
|
this.showDropdown = false
|
|
// 自动带入任务金额到发票金额
|
if (task.transferPrice !== null && task.transferPrice !== undefined) {
|
// 确保金额是数字类型
|
const price = Number(task.transferPrice)
|
if (!isNaN(price) && price > 0) {
|
this.form.invoiceMoney = price.toString()
|
this.maxInvoiceMoney = price
|
console.log('已设置发票金额:', this.form.invoiceMoney)
|
} else {
|
console.log('任务金额无效:', task.transferPrice)
|
}
|
} else {
|
console.log('任务金额为空')
|
}
|
},
|
|
clearSelection() {
|
this.selectedTask = null
|
this.form.serviceOrderId = null
|
this.form.legacyServiceOrderId = null
|
this.form.invoiceMoney = ''
|
this.searchKeyword = ''
|
this.filteredTaskList = []
|
this.maxInvoiceMoney = null
|
},
|
|
validateMoney() {
|
if (!this.form.invoiceMoney) return
|
|
const money = parseFloat(this.form.invoiceMoney)
|
|
if (isNaN(money) || money <= 0) {
|
this.$modal.msgError('请输入有效的金额')
|
this.form.invoiceMoney = ''
|
return
|
}
|
|
// 校验是否超过任务金额
|
if (this.maxInvoiceMoney && money > this.maxInvoiceMoney) {
|
this.$modal.msgError(`发票金额不能超过任务金额¥${this.maxInvoiceMoney}`)
|
this.form.invoiceMoney = this.maxInvoiceMoney.toString()
|
}
|
},
|
handleTypeChange(e) {
|
this.form.invoiceType = e.detail.value
|
},
|
submit() {
|
if (!this.form.serviceOrderId) {
|
this.$modal.msgError("请选择任务")
|
return
|
}
|
if (!this.form.invoiceName) {
|
this.$modal.msgError("请输入发票抬头")
|
return
|
}
|
if (!this.form.invoiceMoney) {
|
this.$modal.msgError("请输入发票金额")
|
return
|
}
|
|
// 再次校验金额
|
const money = parseFloat(this.form.invoiceMoney)
|
if (isNaN(money) || money <= 0) {
|
this.$modal.msgError('请输入有效的金额')
|
return
|
}
|
if (this.maxInvoiceMoney && money > this.maxInvoiceMoney) {
|
this.$modal.msgError(`发票金额不能超过任务金额¥${this.maxInvoiceMoney}`)
|
return
|
}
|
|
addInvoice(this.form).then(res => {
|
this.$modal.msgSuccess("提交成功")
|
setTimeout(() => {
|
// 跳转到发票列表页面
|
uni.redirectTo({
|
url: '/pages/mine/invoice/index'
|
})
|
}, 1500)
|
})
|
}
|
}
|
}
|
</script>
|
|
<style lang="scss">
|
.invoice-apply-container {
|
min-height: 100vh;
|
padding-bottom: 50rpx;
|
|
.cu-form-group .title {
|
min-width: calc(4em + 15px);
|
|
&.required::before {
|
content: '*';
|
color: #f56c6c;
|
margin-right: 4rpx;
|
font-weight: bold;
|
}
|
}
|
|
// ComboBox样式
|
.combo-box-wrapper {
|
position: relative;
|
flex: 1;
|
|
.combo-input {
|
width: 100%;
|
padding-right: 60rpx;
|
}
|
|
.search-icon {
|
position: absolute;
|
right: 20rpx;
|
top: 50%;
|
transform: translateY(-50%);
|
color: #8799a3;
|
font-size: 36rpx;
|
}
|
|
.dropdown-list {
|
position: absolute;
|
top: 100%;
|
left: 0;
|
right: 0;
|
max-height: 500rpx;
|
overflow-y: auto;
|
background: white;
|
border: 2rpx solid #e1e1e1;
|
border-radius: 8rpx;
|
margin-top: 10rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
|
z-index: 999;
|
|
.dropdown-item {
|
padding: 20rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
transition: background 0.2s;
|
|
&:active {
|
background: #f5f5f5;
|
}
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
.item-main {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 8rpx;
|
|
.service-code {
|
font-size: 30rpx;
|
font-weight: bold;
|
color: #1976d2;
|
}
|
|
.task-code {
|
font-size: 22rpx;
|
color: #999;
|
}
|
}
|
|
.item-sub {
|
display: flex;
|
font-size: 24rpx;
|
color: #666;
|
}
|
}
|
|
.empty-result, .loading-result {
|
padding: 40rpx 20rpx;
|
text-align: center;
|
color: #999;
|
}
|
|
.loading-result {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
}
|
}
|
}
|
|
.selected-task {
|
margin: 20rpx 30rpx;
|
|
.label {
|
font-size: 28rpx;
|
color: #666;
|
margin-bottom: 10rpx;
|
}
|
|
.task-card {
|
padding: 20rpx;
|
background: #e8f5e9;
|
border-radius: 8rpx;
|
border: 2rpx solid #4caf50;
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20rpx;
|
padding-bottom: 16rpx;
|
border-bottom: 1rpx solid #c8e6c9;
|
}
|
|
.service-code {
|
font-size: 32rpx;
|
font-weight: bold;
|
color: #2e7d32;
|
}
|
|
.remove-btn {
|
font-size: 40rpx;
|
color: #999;
|
padding: 10rpx;
|
}
|
|
.task-info-detail {
|
.info-row {
|
display: flex;
|
margin-bottom: 12rpx;
|
line-height: 1.6;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.info-label {
|
font-size: 26rpx;
|
color: #666;
|
min-width: 140rpx;
|
flex-shrink: 0;
|
}
|
|
.info-value {
|
font-size: 26rpx;
|
color: #333;
|
flex: 1;
|
word-break: break-all;
|
|
&.text-price {
|
color: #f56c6c;
|
font-weight: bold;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
.money-hint {
|
margin: -10rpx 30rpx 20rpx 30rpx;
|
padding-left: calc(4em + 15px);
|
}
|
}
|
</style>
|