1167 lines
28 KiB
Vue
1167 lines
28 KiB
Vue
<template>
|
||
<div class="container">
|
||
<div class="spacer"></div>
|
||
|
||
<div class="main-content">
|
||
<!-- 资金匹配部分 -->
|
||
<div class="matching-section">
|
||
<div class="section-title">
|
||
<h3>货款匹配</h3>
|
||
<div class="toggle-switch">
|
||
<span class="toggle-label">开启大额匹配</span>
|
||
<label class="switch">
|
||
<input type="checkbox" v-model="matchingType" true-value="large" false-value="small">
|
||
<span class="slider round"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 小额匹配信息 -->
|
||
<div v-if="matchingType === 'small'" class="matching-info">
|
||
<div class="info-item">
|
||
<span class="label">匹配总额:</span>
|
||
<span class="value">¥5,000.00</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">分配笔数:</span>
|
||
<span class="value">3笔</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">单笔范围:</span>
|
||
<span class="value">¥1,000 - ¥5,000</span>
|
||
</div>
|
||
|
||
<button
|
||
@click="createOrder"
|
||
:disabled="creating"
|
||
class="create-btn"
|
||
>
|
||
{{ creating ? '匹配中...' : '开始匹配' }}
|
||
</button>
|
||
|
||
<div class="tips">
|
||
<p>• 系统将为您匹配3笔转账,总金额5000元</p>
|
||
<p>• 优先匹配已完成进货的用户</p>
|
||
<p>• 每笔金额随机分配,确保资金循环</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 大额匹配信息 -->
|
||
<div v-if="matchingType === 'large'" class="matching-info">
|
||
<div class="info-item">
|
||
<span class="label">自定义金额:</span>
|
||
<div class="custom-amount-input">
|
||
<el-input
|
||
v-model="customAmount"
|
||
type="number"
|
||
:min="5000"
|
||
:max="50000"
|
||
step="100"
|
||
placeholder="5000-50000"
|
||
>
|
||
<template #prepend>¥</template>
|
||
</el-input>
|
||
</div>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">分配规则:</span>
|
||
<span class="value">{{ getLargeMatchingRule() }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">预计笔数:</span>
|
||
<span class="value">{{ getLargeMatchingCount() }}笔</span>
|
||
</div>
|
||
|
||
<button
|
||
@click="createOrder"
|
||
:disabled="creating || !isValidCustomAmount"
|
||
class="create-btn"
|
||
>
|
||
{{ creating ? '匹配中...' : '开始匹配' }}
|
||
</button>
|
||
|
||
<div class="tips">
|
||
<p>• 金额范围:5000-50000元</p>
|
||
<p>• 15000元以下:分成多笔随机金额</p>
|
||
<p>• 15000元以上:随机分拆,每笔1000-8000元</p>
|
||
<p>• 优先匹配已完成进货的用户</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 待处理分配 -->
|
||
<div class="pending-section" v-if="pendingAllocations.length > 0">
|
||
<h3>待处理分配</h3>
|
||
<div class="allocation-list">
|
||
<div
|
||
v-for="allocation in pendingAllocations"
|
||
:key="allocation.id"
|
||
class="allocation-card"
|
||
>
|
||
<div class="allocation-info">
|
||
<div class="allocation-header">
|
||
<span class="order-id">订单 #{{ allocation.matching_order_id }}</span>
|
||
<span class="cycle">第{{ allocation.cycle_number }}轮</span>
|
||
</div>
|
||
<div class="allocation-details">
|
||
<p>转账给: <strong>{{ allocation.to_user_real_name }}</strong></p>
|
||
<p>金额: <strong class="amount">¥{{ allocation.amount }}</strong></p>
|
||
<p>总金额: ¥{{ allocation.total_amount }}</p>
|
||
<p class="deadline-info">
|
||
转账时效:
|
||
<span :class="['time-left', allocation.time_status]">
|
||
{{ allocation.time_left }}
|
||
</span>
|
||
<span class="deadline-time">({{ formatDeadline(allocation.deadline) }}前)</span>
|
||
</p>
|
||
<div v-if="!allocation.can_transfer" class="timeout-warning">
|
||
<i class="el-icon-warning"></i>
|
||
<span class="warning-text">{{ allocation.timeout_reason }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="allocation-actions">
|
||
<button
|
||
@click="confirmAllocation(allocation.id, allocation.amount)"
|
||
class="confirm-btn"
|
||
:disabled="processing || !allocation.can_transfer"
|
||
:title="!allocation.can_transfer ? allocation.timeout_reason : ''"
|
||
>
|
||
{{ allocation.can_transfer ? '确认转账' : '无法转账' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- 我的匹配订单 -->
|
||
<div class="orders-section">
|
||
<div class="section-title">
|
||
<h3>匹配订单</h3>
|
||
<router-link to="/transfers">
|
||
<span class="view-all">查看全部 ></span>
|
||
</router-link>
|
||
</div>
|
||
|
||
<div class="orders-list">
|
||
<div
|
||
v-for="order in matchingOrders"
|
||
:key="order.id"
|
||
class="order-card"
|
||
@click="viewOrderDetail(order.id)"
|
||
>
|
||
<div class="order-header">
|
||
<span class="order-id">#{{ order.id }}</span>
|
||
<span v-if="order.is_system_reverse" class="system-reverse-tag">系统反向匹配</span>
|
||
<span :class="['status', order.status]">{{ getStatusText(order.status) }}</span>
|
||
</div>
|
||
<div class="order-info">
|
||
<p>金额: ¥{{ order.amount }}</p>
|
||
<p>发起人: {{ order.initiator_name }}</p>
|
||
<p v-if="!order.is_system_reverse">轮次: {{ order.cycle_count + 1 }}/{{ order.max_cycles }}</p>
|
||
<p v-if="order.is_system_reverse" class="system-note">系统自动发起,向负余额用户补充资金</p>
|
||
<p>创建时间: {{ formatDate(order.created_at) }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="matchingOrders.length === 0" class="empty-state">
|
||
<p>暂无匹配订单</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 订单详情弹窗 -->
|
||
<div v-if="showOrderDetail" class="modal-overlay" @click="closeOrderDetail">
|
||
<div class="modal-content" @click.stop>
|
||
<div class="modal-header">
|
||
<h3>订单详情 #{{ selectedOrder?.order?.id }}</h3>
|
||
<button @click="closeOrderDetail" class="close-btn">×</button>
|
||
</div>
|
||
<div class="modal-body" v-if="selectedOrder">
|
||
<div class="order-summary">
|
||
<p><strong>状态:</strong> {{ getStatusText(selectedOrder.order.status) }}</p>
|
||
<p><strong>金额:</strong> ¥{{ selectedOrder.order.amount }}</p>
|
||
<p><strong>发起人:</strong> {{ selectedOrder.order.initiator_name }}</p>
|
||
<p><strong>轮次:</strong> {{ selectedOrder.order.cycle_count + 1 }}/{{ selectedOrder.order.max_cycles }}</p>
|
||
</div>
|
||
|
||
<div class="allocations-section">
|
||
<h4>分配详情</h4>
|
||
<div class="allocation-timeline">
|
||
<div
|
||
v-for="allocation in selectedOrder.allocations"
|
||
:key="allocation.id"
|
||
class="timeline-item"
|
||
>
|
||
<div class="timeline-content">
|
||
<div class="timeline-header">
|
||
<span class="cycle">第{{ allocation.cycle_number }}轮</span>
|
||
<span :class="['status', allocation.status]">{{ getStatusText(allocation.status) }}</span>
|
||
</div>
|
||
<p>{{ allocation.from_user_name }} → {{ allocation.to_user_name }}</p>
|
||
<p class="amount">¥{{ allocation.amount }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="records-section">
|
||
<h4>操作记录</h4>
|
||
<div class="records-list">
|
||
<div
|
||
v-for="record in selectedOrder.records"
|
||
:key="record.id"
|
||
class="record-item"
|
||
>
|
||
<div class="record-info">
|
||
<span class="action">{{ getActionText(record.action) }}</span>
|
||
<span class="user">{{ record.username }}</span>
|
||
<span class="time">{{ formatDate(record.created_at) }}</span>
|
||
</div>
|
||
<div v-if="record.amount" class="record-amount">¥{{ record.amount }}</div>
|
||
<div v-if="record.note" class="record-note">{{ record.note }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import api from '../utils/api'
|
||
import { uploadURL, getImageUrl, getUploadConfig } from '@/config'
|
||
|
||
export default {
|
||
name: 'Matching',
|
||
data() {
|
||
return {
|
||
stats: {
|
||
userStats: null
|
||
},
|
||
autoStartProcessed: false,
|
||
pendingAllocations: [],
|
||
creating: false,
|
||
processing: false,
|
||
matchingOrders: [],
|
||
showOrderDetail: false,
|
||
selectedOrder: null,
|
||
matchingType: 'small', // 匹配类型:small(小额) 或 large(大额)
|
||
customAmount: '' // 大额匹配自定义金额
|
||
}
|
||
},
|
||
created() {
|
||
if (this.$route.query.autoStart) {
|
||
// 执行匹配逻辑
|
||
this.matchingType = this.$route.query.type || 'small';
|
||
if (this.matchingType === 'large' && this.$route.query.amount) {
|
||
this.customAmount = this.$route.query.amount;
|
||
}
|
||
this.createOrder();
|
||
|
||
// 清除query参数,防止刷新后再次触发
|
||
this.$router.replace({ ...this.$route, query: {} });
|
||
}
|
||
},
|
||
async mounted() {
|
||
await this.loadData()
|
||
|
||
this.handleAutoStartFromHome()
|
||
|
||
// 监听自动开始匹配事件
|
||
const handleAutoStartMatching = async (event) => {
|
||
const { type, amount } = event.detail
|
||
|
||
// 如果有指定匹配类型则切换
|
||
if (type === 'large' && amount) {
|
||
this.matchingType = 'large'
|
||
this.customAmount = amount
|
||
} else {
|
||
this.matchingType = 'small'
|
||
}
|
||
|
||
// 触发匹配
|
||
await this.createOrder()
|
||
}
|
||
|
||
window.addEventListener('autoStartMatching', handleAutoStartMatching)
|
||
},
|
||
methods: {
|
||
/**
|
||
* 确认分配并创建转账记录
|
||
* @param {number} allocationId - 分配ID
|
||
* @param {number} expectedAmount - 预期转账金额
|
||
*/
|
||
async confirmAllocation(allocationId, expectedAmount) {
|
||
try {
|
||
// 获取分配详情和收款用户信息
|
||
const allocationResponse = await api.get(`/matching/allocation/${allocationId}`)
|
||
const allocation = allocationResponse.data.data
|
||
|
||
// 获取收款用户的收款码信息
|
||
const userResponse = await api.get(`/users/payment-info/${allocation.to_user_id}`)
|
||
const userPaymentInfo = userResponse.data.data
|
||
|
||
// 设置转账弹窗数据
|
||
this.transferDialog = {
|
||
visible: true,
|
||
allocationId: allocationId,
|
||
toUser: {
|
||
id: allocation.to_user_id,
|
||
name: allocation.to_user_name,
|
||
wechatQr: userPaymentInfo.wechat_qr,
|
||
alipayQr: userPaymentInfo.alipay_qr,
|
||
unionpayQr: userPaymentInfo.unionpay_qr,
|
||
bankCard: userPaymentInfo.bank_card
|
||
},
|
||
amount: expectedAmount,
|
||
actualAmount: expectedAmount
|
||
}
|
||
} catch (error) {
|
||
console.error('获取转账信息失败:', error)
|
||
this.$message.error('获取转账信息失败')
|
||
}
|
||
},
|
||
formatDeadline(deadlineString) {
|
||
// 根据需要的格式进行处理,示例:
|
||
return new Date(deadlineString).toLocaleString('zh-CN', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
},
|
||
handleAutoStartFromHome() {
|
||
// 获取路由查询参数
|
||
const { autoStart, type, amount } = this.$route.query
|
||
|
||
// 如果参数存在且未处理过
|
||
if (autoStart === 'true' && !this.autoStartProcessed) {
|
||
this.autoStartProcessed = true // 防止重复触发
|
||
|
||
// 根据参数设置匹配类型
|
||
if (type === 'large' && amount) {
|
||
this.matchingType = 'large'
|
||
this.customAmount = amount
|
||
} else {
|
||
this.matchingType = 'small'
|
||
}
|
||
|
||
// 延迟触发匹配,确保页面已渲染完成
|
||
setTimeout(() => {
|
||
this.createOrder()
|
||
}, 500)
|
||
}
|
||
},
|
||
async loadPendingAllocations() {
|
||
try {
|
||
const response = await api.get('/matching/pending-allocations')
|
||
this.pendingAllocations = response.data.data || []
|
||
} catch (error) {
|
||
console.error('加载待处理分配失败:', error)
|
||
}
|
||
},
|
||
async loadData() {
|
||
try {
|
||
await this.loadMatchingOrders()
|
||
await Promise.all([
|
||
this.loadPendingAllocations(),
|
||
])
|
||
} catch (error) {
|
||
console.error('加载数据失败:', error)
|
||
this.$message.error('加载数据失败')
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 创建匹配订单
|
||
*/
|
||
async createOrder() {
|
||
this.creating = true
|
||
try {
|
||
// 构建请求参数
|
||
const requestData = {
|
||
matchingType: this.matchingType
|
||
}
|
||
|
||
// 如果是大额匹配,添加自定义金额
|
||
if (this.matchingType === 'large') {
|
||
if (!this.isValidCustomAmount) {
|
||
this.$message.error('请输入有效的匹配金额(5000-50000元)')
|
||
return
|
||
}
|
||
requestData.customAmount = parseFloat(this.customAmount)
|
||
}
|
||
|
||
await api.post('/matching/create', requestData)
|
||
|
||
const successMessage = this.matchingType === 'small'
|
||
? '小额匹配成功!已为您生成3笔转账分配'
|
||
: `大额匹配成功!已为您生成${this.getLargeMatchingCount()}笔转账分配`
|
||
|
||
this.$message.success(successMessage)
|
||
await this.loadData()
|
||
} catch (error) {
|
||
console.error('创建匹配订单失败:', error)
|
||
|
||
const errorMessage = error.response?.data?.message || '匹配失败,请稍后重试'
|
||
|
||
// 检查是否是审核相关的错误
|
||
if (errorMessage.includes('审核') || errorMessage.includes('上传') || errorMessage.includes('完善')) {
|
||
this.$confirm(errorMessage + ',是否前往个人中心完善资料?', '提示', {
|
||
confirmButtonText: '前往完善',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.$router.push('/editdetailspage')
|
||
}).catch(() => {})
|
||
} else {
|
||
this.$message.error(errorMessage)
|
||
}
|
||
} finally {
|
||
this.creating = false
|
||
}
|
||
},
|
||
|
||
async viewOrderDetail(orderId) {
|
||
try {
|
||
const response = await api.get(`/matching/order/${orderId}`)
|
||
this.selectedOrder = response.data.data
|
||
this.showOrderDetail = true
|
||
} catch (error) {
|
||
console.error('获取订单详情失败:', error)
|
||
this.$message.error('获取订单详情失败')
|
||
}
|
||
},
|
||
|
||
closeOrderDetail() {
|
||
this.showOrderDetail = false
|
||
this.selectedOrder = null
|
||
},
|
||
|
||
async loadMatchingOrders() {
|
||
try {
|
||
const response = await api.get('/matching/my-orders')
|
||
this.matchingOrders = response.data.data || []
|
||
} catch (error) {
|
||
console.error('加载匹配订单失败:', error)
|
||
}
|
||
},
|
||
|
||
getStatusText(status) {
|
||
const statusMap = {
|
||
pending: '待处理',
|
||
matching: '匹配中',
|
||
completed: '已完成',
|
||
cancelled: '已取消',
|
||
confirmed: '已确认',
|
||
rejected: '已拒绝',
|
||
failed: '匹配失败'
|
||
}
|
||
return statusMap[status] || status
|
||
},
|
||
|
||
getActionText(action) {
|
||
const actionMap = {
|
||
join: '加入',
|
||
confirm: '确认',
|
||
reject: '拒绝',
|
||
complete: '完成'
|
||
}
|
||
return actionMap[action] || action
|
||
},
|
||
|
||
formatDate(dateString) {
|
||
return new Date(dateString).toLocaleString('zh-CN')
|
||
},
|
||
|
||
/**
|
||
* 格式化金额显示,确保数字安全
|
||
* @param {number|string|null|undefined} amount - 金额值
|
||
* @returns {string} 格式化后的金额字符串
|
||
*/
|
||
formatAmount(amount) {
|
||
const num = parseFloat(amount)
|
||
return isNaN(num) ? '0.00' : num.toFixed(2)
|
||
},
|
||
|
||
/**
|
||
* 获取大额匹配的分配规则描述
|
||
* @returns {string} 规则描述
|
||
*/
|
||
getLargeMatchingRule() {
|
||
const amount = parseFloat(this.customAmount) || 0
|
||
if (amount <= 0) {
|
||
return '请输入金额'
|
||
} else if (amount < 5000) {
|
||
return '金额不能少于5000元'
|
||
} else if (amount > 50000) {
|
||
return '金额不能超过50000元'
|
||
} else if (amount <= 15000) {
|
||
return '分成多笔随机金额'
|
||
} else {
|
||
return '随机分拆,每笔1000-8000元'
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 获取大额匹配的预计笔数
|
||
* @returns {string} 预计笔数描述
|
||
*/
|
||
getLargeMatchingCount() {
|
||
const amount = parseFloat(this.customAmount) || 0
|
||
if (amount <= 0 || amount < 5000 || amount > 50000) {
|
||
return '0'
|
||
} else if (amount <= 15000) {
|
||
return '3'
|
||
} else {
|
||
// 15000以上随机分拆,估算笔数范围
|
||
const minCount = Math.ceil(amount / 8000) // 按最大单笔8000计算最少笔数
|
||
const maxCount = Math.floor(amount / 1000) // 按最小单笔1000计算最多笔数
|
||
return `${minCount}-${Math.min(maxCount, 10)}` // 限制最大显示笔数为10
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 获取图片URL
|
||
* @param {string} imagePath - 图片路径
|
||
* @returns {string} 完整的图片URL
|
||
*/
|
||
getImageUrl(imagePath) {
|
||
return getImageUrl(imagePath)
|
||
}
|
||
},
|
||
|
||
computed: {
|
||
/**
|
||
* 验证自定义金额是否有效
|
||
* @returns {boolean} 金额是否有效
|
||
*/
|
||
isValidCustomAmount() {
|
||
const amount = parseFloat(this.customAmount)
|
||
return !isNaN(amount) && amount >= 5000 && amount <= 50000
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 基础样式 */
|
||
:root {
|
||
--primary-color: #4361ee;
|
||
--secondary-color: #3f37c9;
|
||
--accent-color: #4895ef;
|
||
--light-color: #f8f9fa;
|
||
--dark-color: #212529;
|
||
--success-color: #4cc9f0;
|
||
--warning-color: #f8961e;
|
||
--danger-color: #f72585;
|
||
--border-radius: 12px;
|
||
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||
--transition: all 0.3s ease;
|
||
}
|
||
|
||
/* 容器布局 */
|
||
.container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 100vh;
|
||
width: 100%;
|
||
margin: 0;
|
||
padding: 0;
|
||
background: linear-gradient(to bottom, #72c9ffae, #f3f3f3);
|
||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
position: relative;
|
||
}
|
||
|
||
.main-content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 16px;
|
||
gap: 16px;
|
||
max-width: 375px;
|
||
margin: 0 auto;
|
||
width: 100%;
|
||
}
|
||
|
||
.spacer {
|
||
height: 40px;
|
||
}
|
||
|
||
/* 通用部分样式 */
|
||
.section-title {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 15px;
|
||
padding: 0;
|
||
}
|
||
|
||
.section-title h3 {
|
||
font-size: 16px;
|
||
color: #333;
|
||
margin: 0;
|
||
width: auto;
|
||
height: 28px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
padding-left: 0;
|
||
}
|
||
|
||
.view-all {
|
||
font-size: 13px;
|
||
color: #999;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* 资金匹配区域 */
|
||
.matching-section {
|
||
background-color: white;
|
||
border-radius: var(--border-radius);
|
||
padding: 16px;
|
||
box-shadow: var(--box-shadow);
|
||
}
|
||
|
||
.matching-info, .orders-list {
|
||
padding-left: 0;
|
||
}
|
||
|
||
/* 切换开关样式 */
|
||
.toggle-switch {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.toggle-label {
|
||
font-size: 12px;
|
||
color: #666;
|
||
width: 77px;
|
||
height: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.switch {
|
||
position: relative;
|
||
display: inline-block;
|
||
width: 34px;
|
||
height: 16px;
|
||
}
|
||
|
||
.switch input {
|
||
opacity: 0;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
.slider {
|
||
position: absolute;
|
||
cursor: pointer;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: #ccc;
|
||
transition: .4s;
|
||
border-radius: 34px;
|
||
}
|
||
|
||
.slider:before {
|
||
position: absolute;
|
||
content: "";
|
||
height: 12px;
|
||
width: 12px;
|
||
left: 2px;
|
||
bottom: 2px;
|
||
background-color: white;
|
||
transition: .4s;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
input:checked + .slider {
|
||
background-color: #a0c4ff;
|
||
}
|
||
|
||
input:focus + .slider {
|
||
box-shadow: 0 0 1px #4CAF50;
|
||
}
|
||
|
||
input:checked + .slider:before {
|
||
transform: translateX(18px);
|
||
}
|
||
|
||
/* 匹配信息样式 */
|
||
.matching-info {
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.info-item .label {
|
||
color: #666;
|
||
font-size: 14px;
|
||
text-align: left;
|
||
width: 80px;
|
||
display: inline-block;
|
||
}
|
||
|
||
.info-item .value {
|
||
color: #333;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.custom-amount-input {
|
||
flex: 1;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
/* 修复输入框字体大小(新增) */
|
||
.custom-amount-input .el-input__inner {
|
||
font-size: 14px !important;
|
||
height: 32px;
|
||
line-height: 32px;
|
||
}
|
||
|
||
.custom-amount-input .el-input-group__prepend {
|
||
font-size: 14px !important;
|
||
}
|
||
|
||
.custom-amount-input .el-input__inner::placeholder {
|
||
font-size: 14px !important;
|
||
}
|
||
|
||
.custom-amount-input .el-input__wrapper {
|
||
border-radius: 12px !important;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 开始匹配按钮 */
|
||
.create-btn {
|
||
width: 128px;
|
||
height: 44px;
|
||
padding: 12px;
|
||
background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 12px !important;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: var(--transition);
|
||
margin: 16px auto;
|
||
box-shadow: var(--box-shadow);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.create-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.create-btn:disabled {
|
||
background: #ccc;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
box-shadow: var(--box-shadow);
|
||
}
|
||
|
||
/* 提示信息 */
|
||
.tips {
|
||
margin-top: 16px;
|
||
padding: 12px;
|
||
background-color: #f8f9fa;
|
||
border-radius: var(--border-radius);
|
||
font-size: 12px;
|
||
color: #666;
|
||
}
|
||
|
||
.tips p {
|
||
margin: 6px 0;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.matching-section,
|
||
.orders-section,
|
||
.order-card,
|
||
.modal-content,
|
||
.tips {
|
||
border-radius: 12px !important;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 订单列表样式 */
|
||
.orders-section {
|
||
background-color: white;
|
||
border-radius: var(--border-radius);
|
||
padding: 16px;
|
||
box-shadow: var(--box-shadow);
|
||
}
|
||
|
||
.orders-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.order-card {
|
||
padding: 12px;
|
||
border: 1px solid #eee;
|
||
border-radius: var(--border-radius);
|
||
cursor: pointer;
|
||
transition: var(--transition);
|
||
}
|
||
|
||
.order-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: var(--box-shadow);
|
||
}
|
||
|
||
.order-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.order-id {
|
||
font-weight: 500;
|
||
color: var(--primary-color);
|
||
}
|
||
|
||
.system-reverse-tag {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 2px 6px;
|
||
border-radius: 10px;
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.status {
|
||
padding: 2px 6px;
|
||
border-radius: 10px;
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
color: white;
|
||
}
|
||
|
||
.status.pending {
|
||
background-color: var(--warning-color);
|
||
}
|
||
|
||
.status.matching {
|
||
background-color: var(--accent-color);
|
||
}
|
||
|
||
.status.completed {
|
||
background-color: var(--success-color);
|
||
}
|
||
|
||
.status.cancelled, .status.failed {
|
||
background-color: var(--danger-color);
|
||
}
|
||
|
||
.order-info p {
|
||
margin: 4px 0;
|
||
font-size: 12px;
|
||
color: #666;
|
||
}
|
||
|
||
.system-note {
|
||
color: var(--accent-color);
|
||
font-style: italic;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 20px;
|
||
color: #999;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 弹窗样式 */
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0,0,0,0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-content {
|
||
background: white;
|
||
border-radius: var(--border-radius);
|
||
max-width: 90%;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
width: 375px;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.modal-header h3 {
|
||
margin: 0;
|
||
font-size: 16px;
|
||
color: #333;
|
||
}
|
||
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
color: #999;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 16px;
|
||
}
|
||
|
||
.order-summary p {
|
||
margin: 8px 0;
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
|
||
.allocations-section, .records-section {
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.allocations-section h4, .records-section h4 {
|
||
font-size: 15px;
|
||
color: #333;
|
||
margin-bottom: 12px;
|
||
padding-bottom: 8px;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.timeline-item {
|
||
border-left: 2px solid var(--accent-color);
|
||
padding-left: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.timeline-content {
|
||
background: #f8f9fa;
|
||
padding: 10px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.timeline-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.cycle {
|
||
font-size: 12px;
|
||
color: #666;
|
||
}
|
||
|
||
.amount {
|
||
font-weight: 500;
|
||
color: var(--success-color);
|
||
}
|
||
|
||
.record-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 8px 0;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.record-info {
|
||
display: flex;
|
||
gap: 10px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.action {
|
||
font-weight: 500;
|
||
color: var(--primary-color);
|
||
}
|
||
|
||
.user {
|
||
color: #666;
|
||
}
|
||
|
||
.time {
|
||
color: #999;
|
||
}
|
||
|
||
.record-amount {
|
||
font-weight: 500;
|
||
color: var(--success-color);
|
||
}
|
||
|
||
.record-note {
|
||
font-size: 12px;
|
||
color: #999;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* 移动端适配 */
|
||
@media (max-width: 375px) {
|
||
.main-content {
|
||
padding: 12px;
|
||
}
|
||
|
||
.matching-section, .orders-section {
|
||
padding: 12px;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 95%;
|
||
}
|
||
|
||
/* 确保输入框在移动端也保持14px */
|
||
.custom-amount-input .el-input__inner,
|
||
.custom-amount-input .el-input-group__prepend,
|
||
.custom-amount-input .el-input__inner::placeholder {
|
||
font-size: 14px !important;
|
||
}
|
||
}
|
||
|
||
.pending-section,
|
||
.orders-section {
|
||
background: white;
|
||
padding: 20px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.pending-section h3,
|
||
.orders-section h3 {
|
||
margin-bottom: 15px;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
.allocation-list,
|
||
.orders-list {
|
||
display: grid;
|
||
gap: 15px;
|
||
}
|
||
|
||
.allocation-card {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 15px;
|
||
border: 1px solid #e1e8ed;
|
||
border-radius: 8px;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.allocation-header {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.order-id,
|
||
.cycle {
|
||
background: #3498db;
|
||
color: white;
|
||
padding: 2px 8px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.allocation-details p {
|
||
margin: 5px 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.deadline-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.time-left {
|
||
font-weight: bold;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.time-left.normal {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
}
|
||
|
||
.time-left.urgent {
|
||
background: #fff3cd;
|
||
color: #856404;
|
||
}
|
||
|
||
.time-left.expired {
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
animation: blink 1s infinite;
|
||
}
|
||
|
||
.deadline-time {
|
||
color: #6c757d;
|
||
font-size: 12px;
|
||
}
|
||
|
||
@keyframes blink {
|
||
0%, 50% { opacity: 1; }
|
||
51%, 100% { opacity: 0.5; }
|
||
}
|
||
|
||
.allocation-actions {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.confirm-btn {
|
||
padding: 8px 16px;
|
||
border: none;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.confirm-btn {
|
||
background: #27ae60;
|
||
color: white;
|
||
}
|
||
|
||
.confirm-btn:hover {
|
||
background: #229954;
|
||
}
|
||
|
||
</style> |