添加 内勤 付款 财务收款 及统计 车辆加车系关键字采集
@@ -0,0 +1,35 @@
|
|||||||
|
import request from '@/utils/http'
|
||||||
|
|
||||||
|
export class dashboardService {
|
||||||
|
//卡片统计
|
||||||
|
static officeclerkcard() {
|
||||||
|
return request.get<any>({
|
||||||
|
url: '/admin/dashboard/officeclerk/card'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//待审批
|
||||||
|
static officeclerkpending() {
|
||||||
|
return request.get<any>({
|
||||||
|
url: '/admin/dashboard/officeclerk/approval'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//业务员备货单统计
|
||||||
|
static salestockingcard() {
|
||||||
|
return request.get<any>({
|
||||||
|
url: '/admin/dashboard/officeclerk/curve'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//待认领
|
||||||
|
static officeclerkunclaimed(params: any) {
|
||||||
|
return request.get<any>({
|
||||||
|
url: '/admin/dashboard/officeclerk/claim',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//财务仪表盘统计
|
||||||
|
static officeclerkchart() {
|
||||||
|
return request.get<any>({
|
||||||
|
url: '/admin/fin/statistics'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,7 +67,28 @@ export class financeService {
|
|||||||
//付款申请-删除
|
//付款申请-删除
|
||||||
static deletePayment(params: any) {
|
static deletePayment(params: any) {
|
||||||
return request.post<Api.Common.AddResponse>({
|
return request.post<Api.Common.AddResponse>({
|
||||||
url: '/admin/payment/destroy',
|
url: '/admin/payment/delete',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 绑定付款记录到收款记录
|
||||||
|
static bindPaymentRecord(params: any) {
|
||||||
|
return request.post<Api.Common.AddResponse>({
|
||||||
|
url: '/admin/receipt/payment',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//收款记录历史
|
||||||
|
static getReceiptHistory(params: any) {
|
||||||
|
return request.get<any>({
|
||||||
|
url: '/admin/receipt/his/list',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//确认收款
|
||||||
|
static confirmReceipt(params: any) {
|
||||||
|
return request.post<Api.Common.AddResponse>({
|
||||||
|
url: '/admin/receipt/credit',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,4 +159,18 @@ export class commonService {
|
|||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
//关键字抓取车辆
|
||||||
|
static getcrawlKeyword(params: any) {
|
||||||
|
return request.get<any>({
|
||||||
|
url: '/common/brand/series/crawlKeyword',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//关键字抓取车辆保存
|
||||||
|
static crawlModelCreate(params: any) {
|
||||||
|
return request.post<Api.Work.AddResponse>({
|
||||||
|
url: '/common/brand/series/crawlModelCreate',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
@@ -14,6 +14,7 @@
|
|||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
:row-key="rowKey"
|
:row-key="rowKey"
|
||||||
|
:row-class-name="rowClassName"
|
||||||
:height="height"
|
:height="height"
|
||||||
:max-height="maxHeight"
|
:max-height="maxHeight"
|
||||||
:show-header="showHeader"
|
:show-header="showHeader"
|
||||||
@@ -123,6 +124,8 @@
|
|||||||
marginTop?: number
|
marginTop?: number
|
||||||
/** 表格大小 */
|
/** 表格大小 */
|
||||||
size?: 'small' | 'default' | 'large'
|
size?: 'small' | 'default' | 'large'
|
||||||
|
/** 行样式类名函数,透传给 el-table 的 row-class-name */
|
||||||
|
rowClassName?: (args: { row: any; rowIndex: number }) => string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<TableProps>(), {
|
const props = withDefaults(defineProps<TableProps>(), {
|
||||||
@@ -146,7 +149,8 @@
|
|||||||
paginationSize: 'default',
|
paginationSize: 'default',
|
||||||
paginationLayout: '',
|
paginationLayout: '',
|
||||||
showHeaderBackground: null,
|
showHeaderBackground: null,
|
||||||
marginTop: 20
|
marginTop: 20,
|
||||||
|
rowClassName: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -0,0 +1,554 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
title="详情"
|
||||||
|
width="800px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div class="payment-detail-dialog" v-loading="loading">
|
||||||
|
<div v-if="paymentData" class="detail-content">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">
|
||||||
|
<span class="border"></span>
|
||||||
|
<span class="title">基本信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<label>创建日期:</label>
|
||||||
|
<span>{{ formatDate(paymentData.created_at) }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="info-item">
|
||||||
|
<label>付款来源:</label>
|
||||||
|
<span>{{ paymentData.country_name || '—' }}</span>
|
||||||
|
</div> -->
|
||||||
|
<div class="info-item">
|
||||||
|
<label>汇款人:</label>
|
||||||
|
<span>{{ paymentData.remitter || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>账户:</label>
|
||||||
|
<span>{{ paymentData.account_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>币种:</label>
|
||||||
|
<span>{{ paymentData.currency_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>金额:</label>
|
||||||
|
<span class="amount">{{ paymentData.amount || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>外汇到账回单:</label>
|
||||||
|
<span>
|
||||||
|
<el-image
|
||||||
|
v-if="paymentData.fx_receipt_file_full"
|
||||||
|
:src="paymentData.fx_receipt_file_full"
|
||||||
|
:preview-src-list="[paymentData.fx_receipt_file_full]"
|
||||||
|
style="width: 100px; height: 100px"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 关联信息 -->
|
||||||
|
<div class="section" v-if="paymentData.payment_apply_id">
|
||||||
|
<div class="section-title">
|
||||||
|
<span class="border"></span>
|
||||||
|
<span class="title">关联信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<label>付款申请ID:</label>
|
||||||
|
<span>{{ paymentData.payment_apply_id }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>阿里订单号/PI号:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.stock[0].pi_no }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>业务员:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.sales_user_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>付款公司:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.account_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>付款来源:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.country_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>汇款人:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.remitter || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>账户:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.account_short_desc || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>币种:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.currency_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>汇率:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.exchange_rate || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>总金额:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.amount || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>汇款行:</label>
|
||||||
|
<span>{{ paymentData.payment_apply.swift || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>客户付款水单:</label>
|
||||||
|
<span>
|
||||||
|
<el-image
|
||||||
|
v-if="paymentData.payment_apply.voucher_file_full"
|
||||||
|
:src="paymentData.payment_apply.voucher_file_full"
|
||||||
|
:preview-src-list="[paymentData.payment_apply.voucher_file_full]"
|
||||||
|
style="width: 100px; height: 100px"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>入账资料:</label>
|
||||||
|
<div v-if="paymentData.payment_apply.submit_files.length > 0" class="files-list">
|
||||||
|
<div
|
||||||
|
v-for="(file, index) in paymentData.payment_apply.submit_files"
|
||||||
|
:key="index"
|
||||||
|
class="file-item"
|
||||||
|
>
|
||||||
|
<div v-if="isImageFile(file.file_full)" class="image-preview">
|
||||||
|
<img
|
||||||
|
:src="file.file_full"
|
||||||
|
:alt="file.file_name"
|
||||||
|
class="preview-image"
|
||||||
|
@click="previewImage(file)"
|
||||||
|
/>
|
||||||
|
<div class="file-info">
|
||||||
|
<span class="file-name">{{ file.file_name }}</span>
|
||||||
|
<div class="file-actions">
|
||||||
|
<!-- <el-button type="text" size="small" @click="previewImage(file)">预览</el-button> -->
|
||||||
|
<el-button type="primary" size="small" @click="downloadFile(file)">下载</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="document-preview">
|
||||||
|
<div class="file-icon">
|
||||||
|
<i class="iconfont-sys"></i>
|
||||||
|
</div>
|
||||||
|
<div class="file-info">
|
||||||
|
<span class="file-name">{{ file.file_name }}</span>
|
||||||
|
<el-button type="primary" size="small" @click="downloadFile(file)">下载</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 收款记录关联 -->
|
||||||
|
<div
|
||||||
|
class="section"
|
||||||
|
v-if="paymentData.receipt_records && paymentData.receipt_records.length"
|
||||||
|
>
|
||||||
|
<div class="section-title">
|
||||||
|
<span class="border"></span>
|
||||||
|
<span class="title">关联收款记录</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="paymentData.receipt_records" style="margin-top: 12px">
|
||||||
|
<el-table-column label="收款记录ID" prop="id" min-width="120" />
|
||||||
|
<el-table-column label="创建日期" prop="created_at" min-width="140">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatDate(scope.row.created_at) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="金额" prop="amount" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatAmount(scope.row.amount, scope.row.currency_name) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" prop="status" min-width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getStatusType(scope.row.status)">
|
||||||
|
{{ getStatusText(scope.row.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 备注信息 -->
|
||||||
|
<div class="section" v-if="paymentData.remark">
|
||||||
|
<div class="section-title">
|
||||||
|
<span class="border"></span>
|
||||||
|
<span class="title">备注信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="remark-content">
|
||||||
|
{{ paymentData.remark }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 无数据状态 -->
|
||||||
|
<div v-else-if="!loading" class="no-data">
|
||||||
|
<i class="iconfont-sys"></i>
|
||||||
|
<span>暂无数据</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { financeService } from '@/api/admin/finance'
|
||||||
|
|
||||||
|
interface PaymentRecord {
|
||||||
|
id: string | number
|
||||||
|
created_at: string
|
||||||
|
country_name: string
|
||||||
|
remitter: string
|
||||||
|
account_name: string
|
||||||
|
currency_name: string
|
||||||
|
amount: number
|
||||||
|
status: string
|
||||||
|
payment_apply_id?: any
|
||||||
|
receipt_records?: any[]
|
||||||
|
payment_apply?: any
|
||||||
|
remark?: string
|
||||||
|
fx_receipt_file_full?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue: boolean
|
||||||
|
paymentId?: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', value: boolean): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
const visible = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value) => emit('update:modelValue', value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const paymentData = ref<PaymentRecord | null>(null)
|
||||||
|
|
||||||
|
// 获取付款记录详情
|
||||||
|
const getPaymentDetail = async () => {
|
||||||
|
if (!props.paymentId) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await financeService.getReceiptDetail({ id: props.paymentId })
|
||||||
|
paymentData.value = res
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取付款记录详情失败:', error)
|
||||||
|
paymentData.value = null
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭对话框
|
||||||
|
const handleClose = () => {
|
||||||
|
visible.value = false
|
||||||
|
paymentData.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印功能
|
||||||
|
const handlePrint = () => {
|
||||||
|
window.print()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (dateStr: string) => {
|
||||||
|
if (!dateStr) return '—'
|
||||||
|
try {
|
||||||
|
return new Date(dateStr).toLocaleString('zh-CN')
|
||||||
|
} catch {
|
||||||
|
return dateStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化金额
|
||||||
|
const formatAmount = (amount: number, currency: string) => {
|
||||||
|
return `${currency || ''} ${amount?.toLocaleString() || 0}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态类型
|
||||||
|
const getStatusType = (status: string): 'success' | 'info' | 'warning' | 'danger' | 'primary' => {
|
||||||
|
const statusMap: Record<string, 'success' | 'info' | 'warning' | 'danger' | 'primary'> = {
|
||||||
|
available: 'success',
|
||||||
|
used: 'info',
|
||||||
|
pending: 'warning',
|
||||||
|
cancelled: 'danger',
|
||||||
|
bound: 'success',
|
||||||
|
unbound: 'info'
|
||||||
|
}
|
||||||
|
return statusMap[status] || 'info'
|
||||||
|
}
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
const statusMap: Record<string, string> = {
|
||||||
|
'available': '可用',
|
||||||
|
'used': '已使用',
|
||||||
|
'pending': '待处理',
|
||||||
|
'cancelled': '已取消',
|
||||||
|
'bound': '已绑定',
|
||||||
|
'unbound': '未绑定'
|
||||||
|
}
|
||||||
|
return statusMap[status] || status
|
||||||
|
}
|
||||||
|
// 判断是否为图片文件
|
||||||
|
const isImageFile = (file: any) => {
|
||||||
|
// 检查文件名
|
||||||
|
if (file.name && /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i.test(file.name)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 检查file_name字段
|
||||||
|
if (file.file_name && file.file_name.startsWith('image/')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// 检查URL中的文件扩展名(去除查询参数)
|
||||||
|
if (file.url) {
|
||||||
|
const urlWithoutParams = file.url.split('?')[0] // 移除查询参数
|
||||||
|
if (/\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i.test(urlWithoutParams)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
const previewImage = (file: any) => {
|
||||||
|
// 创建图片预览弹窗
|
||||||
|
const imagePreview = document.createElement('div')
|
||||||
|
imagePreview.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
cursor: pointer;
|
||||||
|
`
|
||||||
|
const image = document.createElement('img')
|
||||||
|
image.src = file.url
|
||||||
|
image.alt = file.name
|
||||||
|
image.style.cssText = `
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||||
|
`
|
||||||
|
const closeBtn = document.createElement('div')
|
||||||
|
closeBtn.innerHTML = '×'
|
||||||
|
closeBtn.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 30px;
|
||||||
|
color: white;
|
||||||
|
font-size: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 10000;
|
||||||
|
`
|
||||||
|
imagePreview.appendChild(image)
|
||||||
|
imagePreview.appendChild(closeBtn)
|
||||||
|
document.body.appendChild(imagePreview)
|
||||||
|
// 点击关闭
|
||||||
|
const closePreview = () => {
|
||||||
|
document.body.removeChild(imagePreview)
|
||||||
|
}
|
||||||
|
imagePreview.addEventListener('click', closePreview)
|
||||||
|
closeBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
closePreview()
|
||||||
|
})
|
||||||
|
// ESC键关闭
|
||||||
|
const handleKeydown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
closePreview()
|
||||||
|
document.removeEventListener('keydown', handleKeydown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', handleKeydown)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
const downloadFile = async (file: any) => {
|
||||||
|
try {
|
||||||
|
// 对所有文件类型都使用 fetch 下载,确保真正的下载
|
||||||
|
const response = await fetch(file.file_full, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Accept: '*/*'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`下载失败: ${response.status} ${response.statusText}`)
|
||||||
|
}
|
||||||
|
const blob = await response.blob()
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = file.name || file.file_name || 'download'
|
||||||
|
link.style.display = 'none'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
ElMessage.success('下载成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载失败:', error)
|
||||||
|
const errorMessage = error instanceof Error ? error.message : '未知错误'
|
||||||
|
ElMessage.error(`下载失败: ${errorMessage}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听对话框显示状态
|
||||||
|
watch(visible, (newVal) => {
|
||||||
|
if (newVal && props.paymentId) {
|
||||||
|
getPaymentDetail()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听付款ID变化
|
||||||
|
watch(
|
||||||
|
() => props.paymentId,
|
||||||
|
(newId) => {
|
||||||
|
if (visible.value && newId) {
|
||||||
|
getPaymentDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.payment-detail-dialog {
|
||||||
|
.detail-content {
|
||||||
|
.section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #222;
|
||||||
|
|
||||||
|
.border {
|
||||||
|
width: 4px;
|
||||||
|
height: 16px;
|
||||||
|
background: #3d7bff;
|
||||||
|
margin-right: 8px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
//padding: 8px 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666;
|
||||||
|
min-width: 100px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&.amount {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #3d7bff;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.remark-content {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 0;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印样式
|
||||||
|
@media print {
|
||||||
|
.dialog-footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-detail-dialog {
|
||||||
|
.section-title {
|
||||||
|
.border {
|
||||||
|
background: #000 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
title="付款记录详情"
|
||||||
|
width="800px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div class="payment-detail-dialog" v-loading="loading">
|
||||||
|
<div v-if="paymentData" class="detail-content">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">
|
||||||
|
<span class="border"></span>
|
||||||
|
<span class="title">基本信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<label>创建日期:</label>
|
||||||
|
<span>{{ formatDate(paymentData.created_at) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>付款来源:</label>
|
||||||
|
<span>{{ paymentData.country_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>汇款人:</label>
|
||||||
|
<span>{{ paymentData.remitter || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>账户:</label>
|
||||||
|
<span>{{ paymentData.account_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>币种:</label>
|
||||||
|
<span>{{ paymentData.currency_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>金额:</label>
|
||||||
|
<span class="amount">{{ paymentData.amount }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>付款水单:</label>
|
||||||
|
<div>
|
||||||
|
<p v-for="(item, index) in paymentData.voucher_file" :key="index">
|
||||||
|
<el-image
|
||||||
|
v-if="
|
||||||
|
/\.(png|jpe?g|gif|webp|bmp|svg)(\?|$)/i.test(
|
||||||
|
(item.file_full || '').split('?')[0]
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:src="item.file_full"
|
||||||
|
:preview-src-list="item.file_full ? [item.file_full] : []"
|
||||||
|
style="width: 100px; height: 100px"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
v-else
|
||||||
|
:href="item.file_full"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
style="
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>{{ item.file_name }}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 关联信息 -->
|
||||||
|
<div class="section" v-if="paymentData.payment_apply_id">
|
||||||
|
<div class="section-title">
|
||||||
|
<span class="border"></span>
|
||||||
|
<span class="title">关联信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<label>付款申请ID:</label>
|
||||||
|
<span>{{ paymentData.payment_apply_id }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>关联状态:</label>
|
||||||
|
<el-tag :type="getStatusType(paymentData.status)">
|
||||||
|
{{ getStatusText(paymentData.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 收款记录关联 -->
|
||||||
|
<div
|
||||||
|
class="section"
|
||||||
|
v-if="paymentData.receipt_records && paymentData.receipt_records.length"
|
||||||
|
>
|
||||||
|
<div class="section-title">
|
||||||
|
<span class="border"></span>
|
||||||
|
<span class="title">关联收款记录</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="paymentData.receipt_records" style="margin-top: 12px">
|
||||||
|
<el-table-column label="收款记录ID" prop="id" min-width="120" />
|
||||||
|
<el-table-column label="创建日期" prop="created_at" min-width="140">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatDate(scope.row.created_at) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="金额" prop="amount" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatAmount(scope.row.amount, scope.row.currency_name) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" prop="status" min-width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getStatusType(scope.row.status)">
|
||||||
|
{{ getStatusText(scope.row.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 备注信息 -->
|
||||||
|
<div class="section" v-if="paymentData.remark">
|
||||||
|
<div class="section-title">
|
||||||
|
<span class="border"></span>
|
||||||
|
<span class="title">备注信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="remark-content">
|
||||||
|
{{ paymentData.remark }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 无数据状态 -->
|
||||||
|
<div v-else-if="!loading" class="no-data">
|
||||||
|
<i class="iconfont-sys"></i>
|
||||||
|
<span>暂无数据</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">关闭</el-button>
|
||||||
|
</div> -->
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { financeService } from '@/api/admin/finance'
|
||||||
|
|
||||||
|
interface PaymentRecord {
|
||||||
|
id: string | number
|
||||||
|
created_at: string
|
||||||
|
country_name: string
|
||||||
|
remitter: string
|
||||||
|
account_name: string
|
||||||
|
currency_name: string
|
||||||
|
amount: number
|
||||||
|
status: string
|
||||||
|
payment_apply_id?: string | number
|
||||||
|
receipt_records?: any[]
|
||||||
|
voucher_file?: any[]
|
||||||
|
submit_files?: string[]
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue: boolean
|
||||||
|
paymentId?: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', value: boolean): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
const visible = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value) => emit('update:modelValue', value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const paymentData = ref<PaymentRecord | null>(null)
|
||||||
|
|
||||||
|
// 获取付款记录详情
|
||||||
|
const getPaymentDetail = async () => {
|
||||||
|
if (!props.paymentId) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await financeService.getPaymentDetail({ id: props.paymentId })
|
||||||
|
paymentData.value = res
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取付款记录详情失败:', error)
|
||||||
|
paymentData.value = null
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭对话框
|
||||||
|
const handleClose = () => {
|
||||||
|
visible.value = false
|
||||||
|
paymentData.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印功能
|
||||||
|
const handlePrint = () => {
|
||||||
|
window.print()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (dateStr: string) => {
|
||||||
|
if (!dateStr) return '—'
|
||||||
|
try {
|
||||||
|
return new Date(dateStr).toLocaleString('zh-CN')
|
||||||
|
} catch {
|
||||||
|
return dateStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化金额
|
||||||
|
const formatAmount = (amount: number, currency: string) => {
|
||||||
|
return `${currency || ''} ${amount?.toLocaleString() || 0}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态类型
|
||||||
|
const getStatusType = (status: string): 'success' | 'info' | 'warning' | 'danger' | 'primary' => {
|
||||||
|
const statusMap: Record<string, 'success' | 'info' | 'warning' | 'danger' | 'primary'> = {
|
||||||
|
available: 'success',
|
||||||
|
used: 'info',
|
||||||
|
pending: 'warning',
|
||||||
|
cancelled: 'danger',
|
||||||
|
bound: 'success',
|
||||||
|
unbound: 'info'
|
||||||
|
}
|
||||||
|
return statusMap[status] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
const statusMap: Record<string, string> = {
|
||||||
|
'available': '可用',
|
||||||
|
'used': '已使用',
|
||||||
|
'pending': '待处理',
|
||||||
|
'cancelled': '已取消',
|
||||||
|
'bound': '已绑定',
|
||||||
|
'unbound': '未绑定'
|
||||||
|
}
|
||||||
|
return statusMap[status] || status
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听对话框显示状态
|
||||||
|
watch(visible, (newVal) => {
|
||||||
|
if (newVal && props.paymentId) {
|
||||||
|
getPaymentDetail()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听付款ID变化
|
||||||
|
watch(() => props.paymentId, (newId) => {
|
||||||
|
if (visible.value && newId) {
|
||||||
|
getPaymentDetail()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.payment-detail-dialog {
|
||||||
|
.detail-content {
|
||||||
|
.section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #222;
|
||||||
|
|
||||||
|
.border {
|
||||||
|
width: 4px;
|
||||||
|
height: 16px;
|
||||||
|
background: #3d7bff;
|
||||||
|
margin-right: 8px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// padding: 8px 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666;
|
||||||
|
min-width: 100px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&.amount {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #3d7bff;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.remark-content {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 0;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印样式
|
||||||
|
@media print {
|
||||||
|
.dialog-footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-detail-dialog {
|
||||||
|
.section-title {
|
||||||
|
.border {
|
||||||
|
background: #000 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
title="选择付款记录"
|
||||||
|
width="80%"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div class="payment-record-dialog">
|
||||||
|
<!-- 搜索栏 -->
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="请输入汇款人、账户或金额"
|
||||||
|
clearable
|
||||||
|
style="width: 300px"
|
||||||
|
@keyup.enter="getPaymentRecords"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<el-icon class="clickable" @click="getPaymentRecords"><Search /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<el-button type="primary" @click="getPaymentRecords">搜索</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 付款记录表格 -->
|
||||||
|
<el-table
|
||||||
|
ref="tableRef"
|
||||||
|
:data="paymentRecords"
|
||||||
|
:loading="loading"
|
||||||
|
row-key="id"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
@row-click="handleRowClick"
|
||||||
|
highlight-current-row
|
||||||
|
style="margin-top: 16px"
|
||||||
|
>
|
||||||
|
<el-table-column label="创建日期" prop="created_at" min-width="160" />
|
||||||
|
<el-table-column label="付款来源" prop="country_name" min-width="120" />
|
||||||
|
<el-table-column label="汇款人" prop="remitter" min-width="120" />
|
||||||
|
<el-table-column label="账户" prop="account_name" min-width="120" />
|
||||||
|
<el-table-column label="账币" prop="currency_name" min-width="120" />
|
||||||
|
<el-table-column label="金额" prop="amount" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatAmount(scope.row.amount, scope.row.currency_name) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="选择" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-checkbox
|
||||||
|
v-model="checkboxStates[scope.row.id]"
|
||||||
|
@change="(val) => handleCheckboxChange(scope.row, !!val)"
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="pagination.currentPage"
|
||||||
|
v-model:page-size="pagination.pageSize"
|
||||||
|
:total="pagination.total"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="handleClose">取消</el-button>
|
||||||
|
<el-button type="primary" :disabled="!selectedRecord" @click="handleConfirm">
|
||||||
|
确认绑定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { financeService } from '@/api/admin/finance'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
interface PaymentRecord {
|
||||||
|
id: string | number
|
||||||
|
created_at: string
|
||||||
|
country_name: string
|
||||||
|
remitter: string
|
||||||
|
account_name: string
|
||||||
|
currency_name: string
|
||||||
|
amount: number
|
||||||
|
status: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue: boolean
|
||||||
|
receiptId?: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', value: boolean): void
|
||||||
|
(e: 'confirm', record: PaymentRecord): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
const visible = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (value) => emit('update:modelValue', value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const searchKeyword = ref('')
|
||||||
|
// const paymentRecords = ref<PaymentRecord[]>([])
|
||||||
|
const paymentRecords = ref<any[]>([])
|
||||||
|
const selectedRecord = ref<PaymentRecord | null>(null)
|
||||||
|
// const selectedRecord = ref<any[]>([])
|
||||||
|
const selectedRecordId = ref<any>(null)
|
||||||
|
const checkboxStates = reactive<Record<string | number, boolean>>({})
|
||||||
|
const tableRef = ref()
|
||||||
|
|
||||||
|
const pagination = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取付款记录列表
|
||||||
|
const getPaymentRecords = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
current: pagination.currentPage,
|
||||||
|
size: pagination.pageSize,
|
||||||
|
keyword: searchKeyword.value,
|
||||||
|
scene: 'correlation',
|
||||||
|
receipt_id: props.receiptId,
|
||||||
|
correlation: 'unbind' // 只显示可用的付款记录
|
||||||
|
}
|
||||||
|
|
||||||
|
const { records, current, size, total } = await financeService.getPaymentList(params)
|
||||||
|
paymentRecords.value = records || []
|
||||||
|
// 初始化 checkbox 状态
|
||||||
|
records?.forEach((record: any) => {
|
||||||
|
checkboxStates[record.id] = false
|
||||||
|
})
|
||||||
|
Object.assign(pagination, { currentPage: current, pageSize: size, total })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取付款记录失败:', error)
|
||||||
|
ElMessage.error('获取付款记录失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理选择变化(保留兼容性,单选模式下不使用此方法)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const handleSelectionChange = (_selection: PaymentRecord[]) => {
|
||||||
|
// 单选模式下不使用此方法
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 checkbox 变化(单选逻辑)
|
||||||
|
const handleCheckboxChange = (record: PaymentRecord, checked: boolean) => {
|
||||||
|
if (checked) {
|
||||||
|
// 先清除所有选中状态(单选逻辑)
|
||||||
|
Object.keys(checkboxStates).forEach((id) => {
|
||||||
|
checkboxStates[id] = false
|
||||||
|
})
|
||||||
|
// 选中当前项
|
||||||
|
checkboxStates[record.id] = true
|
||||||
|
selectedRecord.value = record
|
||||||
|
selectedRecordId.value = record.id
|
||||||
|
} else {
|
||||||
|
// 取消选中
|
||||||
|
selectedRecord.value = null
|
||||||
|
selectedRecordId.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理行点击
|
||||||
|
const handleRowClick = (record: PaymentRecord) => {
|
||||||
|
// 如果点击的是已选中的项,则取消选中;否则选中该项
|
||||||
|
if (selectedRecordId.value === record.id) {
|
||||||
|
// 取消选中
|
||||||
|
selectedRecord.value = null
|
||||||
|
selectedRecordId.value = null
|
||||||
|
checkboxStates[record.id] = false
|
||||||
|
} else {
|
||||||
|
// 先清除所有选中状态
|
||||||
|
Object.keys(checkboxStates).forEach((id) => {
|
||||||
|
checkboxStates[id] = false
|
||||||
|
})
|
||||||
|
// 选中当前项
|
||||||
|
selectedRecord.value = record
|
||||||
|
selectedRecordId.value = record.id
|
||||||
|
checkboxStates[record.id] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页处理
|
||||||
|
const handleSizeChange = (size: number) => {
|
||||||
|
pagination.pageSize = size
|
||||||
|
pagination.currentPage = 1
|
||||||
|
getPaymentRecords()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (page: number) => {
|
||||||
|
pagination.currentPage = page
|
||||||
|
getPaymentRecords()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认绑定
|
||||||
|
const handleConfirm = () => {
|
||||||
|
if (!selectedRecord.value) {
|
||||||
|
ElMessage.warning('请选择一条付款记录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emit('confirm', selectedRecord.value)
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭对话框
|
||||||
|
const handleClose = () => {
|
||||||
|
visible.value = false
|
||||||
|
selectedRecord.value = null
|
||||||
|
selectedRecordId.value = null
|
||||||
|
Object.keys(checkboxStates).forEach((id) => {
|
||||||
|
checkboxStates[id] = false
|
||||||
|
})
|
||||||
|
searchKeyword.value = ''
|
||||||
|
pagination.currentPage = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化金额显示
|
||||||
|
const formatAmount = (amount: number, currency: string) => {
|
||||||
|
return `${currency} ${amount?.toLocaleString() || 0}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态类型(暂未使用)
|
||||||
|
// const getStatusType = (status: string) => {
|
||||||
|
// const statusMap: Record<string, string> = {
|
||||||
|
// available: 'success',
|
||||||
|
// used: 'info',
|
||||||
|
// pending: 'warning',
|
||||||
|
// cancelled: 'danger'
|
||||||
|
// }
|
||||||
|
// return statusMap[status] || 'info'
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 获取状态文本(暂未使用)
|
||||||
|
// const getStatusText = (status: string) => {
|
||||||
|
// const statusMap: Record<string, string> = {
|
||||||
|
// available: '可用',
|
||||||
|
// used: '已使用',
|
||||||
|
// pending: '待处理',
|
||||||
|
// cancelled: '已取消'
|
||||||
|
// }
|
||||||
|
// return statusMap[status] || status
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 监听对话框显示状态
|
||||||
|
watch(visible, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
getPaymentRecords()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.payment-record-dialog {
|
||||||
|
.search-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<template>
|
||||||
|
<div class="approval-timeline">
|
||||||
|
<div v-for="(rec, idx) in records" :key="idx" class="timeline-item">
|
||||||
|
<div class="left">
|
||||||
|
<div class="avatar-box">
|
||||||
|
<span class="initial">{{ rec.updated_by?.slice(0, 1) || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<div class="row name-role">
|
||||||
|
<span class="name">编辑人:{{ rec.updated_by }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="row time">{{ rec.updated_at }}</div>
|
||||||
|
<div class="card-box">
|
||||||
|
<el-card>
|
||||||
|
<h4>汇款人:{{ rec.remitter }}</h4>
|
||||||
|
<p
|
||||||
|
><span>付款来源:{{ rec.payment_apply_id }}</span>
|
||||||
|
<span>账户:{{ rec.account_short_desc }}</span>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
><span>金额:{{ rec.amount }} </span><span>币种:{{ rec.currency_name }}</span></p
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
><span>折让金额:{{ rec.discount_amount }}</span
|
||||||
|
><span>实际汇率:{{ rec.exchange_rate }}</span>
|
||||||
|
</p>
|
||||||
|
<p>总金额(人民币)实际汇率:{{ rec.total_amount_cny }}</p>
|
||||||
|
<p>收款时间:{{ rec.received_at }}</p>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
interface ApprovalRecordItem {
|
||||||
|
account_id?: string
|
||||||
|
account_name?: string
|
||||||
|
amount?: string
|
||||||
|
arrived_at?: string
|
||||||
|
country_id?: string
|
||||||
|
country_name?: string
|
||||||
|
created_at?: string
|
||||||
|
created_by?: string
|
||||||
|
currency_id?: string
|
||||||
|
currency_name?: string
|
||||||
|
discount_amount?: string
|
||||||
|
exchange_rate?: string
|
||||||
|
fx_receipt_file_full?: string
|
||||||
|
account_short_desc?: string
|
||||||
|
id?: string
|
||||||
|
is_domestic_return?: string
|
||||||
|
payment_apply_id?: string
|
||||||
|
receipt_id?: string
|
||||||
|
receipt_no?: string
|
||||||
|
received_at?: string
|
||||||
|
remitter?: string
|
||||||
|
total_amount_cny?: string
|
||||||
|
updated_at?: string
|
||||||
|
updated_by?: string
|
||||||
|
voucher_no?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<{ records: ApprovalRecordItem[] }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
/* 审批记录 时间线 */
|
||||||
|
.approval-timeline {
|
||||||
|
padding: 8px 4px 8px 0;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.timeline-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.timeline-item + .timeline-item {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
width: 46px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.avatar-box {
|
||||||
|
width: 46px;
|
||||||
|
height: 46px;
|
||||||
|
background: #3d7bff;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #fff;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.status-dot {
|
||||||
|
position: absolute;
|
||||||
|
right: -4px;
|
||||||
|
bottom: -4px;
|
||||||
|
font-size: 9px;
|
||||||
|
padding: 3px 1px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #22c55e;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.status-dot.pending {
|
||||||
|
background: #3471ff;
|
||||||
|
padding: 3px 3px;
|
||||||
|
}
|
||||||
|
.status-dot.rejected {
|
||||||
|
background: #ff0000;
|
||||||
|
padding: 3px 3px;
|
||||||
|
}
|
||||||
|
/* 连接下一个节点的虚线 */
|
||||||
|
.timeline-item:not(:last-child) .left::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 52px; /* avatar 46px + 间距 6px */
|
||||||
|
left: 23px; /* 盒子宽度一半,确保竖线居中 */
|
||||||
|
width: 0;
|
||||||
|
height: calc(100% + 116px); /* 加上相邻项之间的间距 */
|
||||||
|
border-left: 2px dotted #bfbfbf;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
flex: 1;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
.row.name-role {
|
||||||
|
color: #222;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.role {
|
||||||
|
color: #666;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
.time {
|
||||||
|
color: #888;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.empty {
|
||||||
|
color: #999;
|
||||||
|
padding: 8px 0 4px 4px;
|
||||||
|
}
|
||||||
|
.card-box p{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -11,6 +11,22 @@ import { AppRouteRecord } from '@/types/router'
|
|||||||
* 可以是 i18n 的 key,也可以是字符串,比如:'用户列表'
|
* 可以是 i18n 的 key,也可以是字符串,比如:'用户列表'
|
||||||
*/
|
*/
|
||||||
export const asyncRoutes: AppRouteRecord[] = [
|
export const asyncRoutes: AppRouteRecord[] = [
|
||||||
|
{
|
||||||
|
path: '/dashboard/console/finance',
|
||||||
|
name: 'Consolefinance',
|
||||||
|
component: RoutesAlias.consolefinance,
|
||||||
|
meta: {
|
||||||
|
title: '仪表盘'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dashboard/console/backoffice',
|
||||||
|
name: 'Backoffice',
|
||||||
|
component: RoutesAlias.backoffice,
|
||||||
|
meta: {
|
||||||
|
title: '仪表盘'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/admin/inquiry_system',
|
path: '/admin/inquiry_system',
|
||||||
name: 'Inquiry_system',
|
name: 'Inquiry_system',
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ export enum RoutesAlias {
|
|||||||
StockingAdd = '/crmpc/stocking/Add', // 创建备货单
|
StockingAdd = '/crmpc/stocking/Add', // 创建备货单
|
||||||
StockingShow = '/crmpc/stocking/show', // 备货单详情
|
StockingShow = '/crmpc/stocking/show', // 备货单详情
|
||||||
StockingFinalAdd = '/crmpc/stocking/final_add', // 最终备货单添加
|
StockingFinalAdd = '/crmpc/stocking/final_add', // 最终备货单添加
|
||||||
StockingFinalShow = '/crmpc/stocking/final_show' // 最终备货单详情
|
StockingFinalShow = '/crmpc/stocking/final_show', // 最终备货单详情
|
||||||
|
consolefinance = '/dashboard/console/finance', //财务仪表盘
|
||||||
|
backoffice = '/dashboard/console/backoffice' // 内勤仪表盘
|
||||||
}
|
}
|
||||||
|
|
||||||
// 主页路由
|
// 主页路由
|
||||||
|
|||||||
@@ -0,0 +1,300 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stocking-page">
|
||||||
|
<div class="stocking-toolbar boxshadow">
|
||||||
|
<div class="toolbar-left"></div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<el-input
|
||||||
|
v-model="keyword"
|
||||||
|
placeholder="请输入客户名称"
|
||||||
|
clearable
|
||||||
|
style="width: 260px"
|
||||||
|
@keyup.enter="getList"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<el-icon class="clickable" @click="getList"><Search /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="boxshadow">
|
||||||
|
<el-tabs v-model="activeTab">
|
||||||
|
<el-tab-pane label="未认领" name="not"></el-tab-pane>
|
||||||
|
<el-tab-pane label="已认领" name="already"></el-tab-pane>
|
||||||
|
<el-tab-pane label="超时未认领" name="timeout"></el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<ArtTable
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="id"
|
||||||
|
:loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
:currentPage="pagination.currentPage"
|
||||||
|
:pageSize="pagination.pageSize"
|
||||||
|
:total="pagination.total"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:row-class-name="tableRowClassName"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<ElTableColumn label="创建日期" prop="created_at" min-width="160" />
|
||||||
|
<!-- <ElTableColumn label="付款来源" prop="country_name" min-width="120" /> -->
|
||||||
|
<ElTableColumn label="汇款人" prop="remitter" min-width="120" />
|
||||||
|
<ElTableColumn label="账户" prop="account_name" min-width="120" />
|
||||||
|
<ElTableColumn label="账币" prop="currency_name" min-width="120" />
|
||||||
|
<ElTableColumn label="金额" prop="amount" min-width="120" />
|
||||||
|
<ElTableColumn label="计时" prop="amount" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<span
|
||||||
|
v-if="!scope.row.payment_apply_id"
|
||||||
|
:class="getCountdownClass(scope.row.claim_timeout_time)"
|
||||||
|
>
|
||||||
|
<span class="custom-clock-icon" style="vertical-align: middle; margin-right: 4px">
|
||||||
|
<i class="iconfont-sys"></i>
|
||||||
|
</span>
|
||||||
|
{{ getCountdown(scope.row.claim_timeout_time) }}
|
||||||
|
</span>
|
||||||
|
<span v-else class="countdown-success">已认领</span>
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
|
<ElTableColumn label="操作" fixed="right" min-width="140" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<ElRow :gutter="10" justify="center">
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
underline="never"
|
||||||
|
@click="handleView(scope.row)"
|
||||||
|
style="margin-right: 8px"
|
||||||
|
>
|
||||||
|
<i class="iconfont-sys"></i> 查看
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="!scope.row.payment_apply_id"
|
||||||
|
type="success"
|
||||||
|
underline="never"
|
||||||
|
@click="handleClaim(scope.row)"
|
||||||
|
>
|
||||||
|
<i class="iconfont-sys"></i> 认领
|
||||||
|
</el-link>
|
||||||
|
</ElRow>
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
|
</template>
|
||||||
|
</ArtTable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 付款记录选择对话框 -->
|
||||||
|
<PaymentRecordDialog
|
||||||
|
v-model="paymentDialogVisible"
|
||||||
|
:receipt-id="currentReceiptId"
|
||||||
|
@confirm="handlePaymentRecordConfirm"
|
||||||
|
/>
|
||||||
|
<!-- 付款记录详情查看对话框 -->
|
||||||
|
<ClaimRecordDetailDialog v-model="detailDialogVisible" :payment-id="currentPaymentId" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { manageService } from '@/api/adminApi'
|
||||||
|
import { financeService } from '@/api/admin/finance'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import { useAuth } from '@/composables/useAuth'
|
||||||
|
import PaymentRecordDialog from '@/components/custom/PaymentRecordDialog.vue'
|
||||||
|
import ClaimRecordDetailDialog from '@/components/custom/ClaimRecordDetailDialog.vue'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
defineOptions({ name: 'Adminclaim' })
|
||||||
|
const { hasAuth } = useAuth()
|
||||||
|
const loading = ref(false)
|
||||||
|
const keyword = ref('')
|
||||||
|
const tableRef = ref()
|
||||||
|
const tableData = ref<any[]>([])
|
||||||
|
const pagination = reactive({ currentPage: 1, pageSize: 10, total: 0 })
|
||||||
|
const activeTab = ref('not')
|
||||||
|
const detailDialogVisible = ref(false)
|
||||||
|
// 付款记录对话框相关
|
||||||
|
const paymentDialogVisible = ref(false)
|
||||||
|
const currentReceiptId = ref<string | number>('')
|
||||||
|
watch([keyword], () => {
|
||||||
|
pagination.currentPage = 1
|
||||||
|
getList()
|
||||||
|
// 不自动请求,保持与截图行为一致,点击搜索或回车触发
|
||||||
|
})
|
||||||
|
watch([activeTab], () => {
|
||||||
|
pagination.currentPage = 1
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const { currentPage, pageSize } = pagination
|
||||||
|
const params = {
|
||||||
|
current: currentPage,
|
||||||
|
size: pageSize,
|
||||||
|
payment_apply: activeTab.value,
|
||||||
|
keyword: keyword.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const { records, current, size, total } = await financeService.getReceiptList(params)
|
||||||
|
tableData.value = records || []
|
||||||
|
Object.assign(pagination, { currentPage: current, pageSize: size, total })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取列表失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSizeChange = (size: number) => {
|
||||||
|
pagination.pageSize = size
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
const handleCurrentChange = (page: number) => {
|
||||||
|
pagination.currentPage = page
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
const currentPaymentId = ref<string | number>('')
|
||||||
|
const handleView = async (params: any) => {
|
||||||
|
detailDialogVisible.value = true
|
||||||
|
currentPaymentId.value = params.id
|
||||||
|
await financeService.getReceiptDetail(params)
|
||||||
|
}
|
||||||
|
// 处理认领操作
|
||||||
|
const handleClaim = (row: any) => {
|
||||||
|
currentReceiptId.value = row.id
|
||||||
|
paymentDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理付款记录确认绑定
|
||||||
|
const handlePaymentRecordConfirm = async (paymentRecord: any) => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
id: currentReceiptId.value,
|
||||||
|
payment_apply_id: paymentRecord.id
|
||||||
|
}
|
||||||
|
|
||||||
|
await financeService.bindPaymentRecord(params)
|
||||||
|
ElMessage.success('认领成功')
|
||||||
|
getList() // 刷新列表
|
||||||
|
} catch (error) {
|
||||||
|
console.error('认领失败:', error)
|
||||||
|
ElMessage.error('认领失败,请稍后重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = () => {}
|
||||||
|
const timer = ref(0)
|
||||||
|
const refreshKey = ref(0)
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
timer.value = window.setInterval(() => {
|
||||||
|
refreshKey.value++
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
function getCountdown(targetDate: string) {
|
||||||
|
// 让 refreshKey 成为依赖,确保每秒刷新
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
refreshKey.value
|
||||||
|
const now = dayjs()
|
||||||
|
const target = dayjs(targetDate, 'YYYY-MM-DD HH:mm:ss')
|
||||||
|
if (!target.isValid()) return '时间格式错误'
|
||||||
|
const diff = target.diff(now)
|
||||||
|
if (diff <= 0) return '已超时'
|
||||||
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
|
||||||
|
const hours = Math.floor((diff / (1000 * 60 * 60)) % 24)
|
||||||
|
const minutes = Math.floor((diff / (1000 * 60)) % 60)
|
||||||
|
const seconds = Math.floor((diff / 1000) % 60)
|
||||||
|
return `${days > 0 ? days + '天' : ''}${hours}小时${minutes}分${seconds}秒`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCountdownClass(targetDate: string) {
|
||||||
|
const now = dayjs()
|
||||||
|
const target = dayjs(targetDate, 'YYYY-MM-DD HH:mm:ss')
|
||||||
|
if (!target.isValid() || target.diff(now) <= 0) {
|
||||||
|
return 'countdown-danger'
|
||||||
|
}
|
||||||
|
return 'countdown-success'
|
||||||
|
}
|
||||||
|
const tableRowClassName = ({ row }: { row: any }) => {
|
||||||
|
const flag = Number(row?.claim_timeout)
|
||||||
|
return flag === 1 ? 'row-claim-timeout' : ''
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
:deep(.row-claim-timeout) {
|
||||||
|
background-color: #ffc9c9 !important;
|
||||||
|
}
|
||||||
|
:deep(.el-tabs__nav-wrap:after){
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.countdown-danger {
|
||||||
|
color: #ff0000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown-success {
|
||||||
|
color: #00a43f !important;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.stocking-page {
|
||||||
|
background: var(--art-main-bg-color);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.stocking-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--art-main-bg-color);
|
||||||
|
}
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.toolbar-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
// 创建收款记录 - 弹窗样式对齐设计图
|
||||||
|
.receipt-dialog {
|
||||||
|
.receipt-form {
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-demo {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: flex-end;
|
||||||
|
// border: 1px dashed #dcdfe6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.avatarimg {
|
||||||
|
width: 60px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
width: 200px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.receipt-dialog .el-dialog__footer) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,832 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stocking-page">
|
||||||
|
<div class="stocking-toolbar boxshadow">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<ElButton
|
||||||
|
type="primary"
|
||||||
|
v-ripple
|
||||||
|
@click="handleCreate"
|
||||||
|
v-if="hasAuth('receipt_records@add')"
|
||||||
|
>
|
||||||
|
创建收款记录
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<el-input
|
||||||
|
v-model="keyword"
|
||||||
|
placeholder="请输入客户名称"
|
||||||
|
clearable
|
||||||
|
style="width: 260px"
|
||||||
|
@keyup.enter="getList"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<el-icon class="clickable" @click="getList"><Search /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="boxshadow">
|
||||||
|
<ArtTable
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="id"
|
||||||
|
:key="tableData.length"
|
||||||
|
:loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
:currentPage="pagination.currentPage"
|
||||||
|
:pageSize="pagination.pageSize"
|
||||||
|
:total="pagination.total"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:row-class-name="tableRowClassName"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<ElTableColumn label="创建日期" prop="created_at" min-width="140" />
|
||||||
|
<!-- <ElTableColumn label="付款来源" prop="country_name" min-width="160" /> -->
|
||||||
|
<ElTableColumn label="汇款人" prop="remitter" min-width="120" />
|
||||||
|
<ElTableColumn label="账户" prop="account_short_desc" min-width="120" />
|
||||||
|
<ElTableColumn label="币种" prop="currency_name" min-width="120" />
|
||||||
|
<ElTableColumn label="金额" prop="amount" min-width="120" />
|
||||||
|
<ElTableColumn label="折让金额(含手续费)" prop="discount_amount" min-width="120" />
|
||||||
|
<ElTableColumn label="认领状态" prop="payment_apply_id" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<span
|
||||||
|
v-if="scope.row.payment_apply_id == 0 || scope.row.payment_apply_id == null"
|
||||||
|
class="text-danger"
|
||||||
|
>
|
||||||
|
未认领
|
||||||
|
</span>
|
||||||
|
<span v-else class="text-success">已认领</span>
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
|
<ElTableColumn label="入账" prop="id" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.received_at">{{ scope.row.received_at }}</span>
|
||||||
|
<el-button v-else type="primary" size="small" @click="handlebtn(scope.row)">
|
||||||
|
确认入账
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
|
<ElTableColumn label="操作" fixed="right" min-width="180" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<ElRow :gutter="10" justify="center">
|
||||||
|
<el-link
|
||||||
|
v-if="hasAuth('receipt_records@show')"
|
||||||
|
type="primary"
|
||||||
|
underline="never"
|
||||||
|
@click="handleView(scope.row)"
|
||||||
|
style="margin-right: 8px"
|
||||||
|
>
|
||||||
|
<i class="iconfont-sys"></i> 查看
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="hasAuth('receipt_records@update')"
|
||||||
|
type="success"
|
||||||
|
underline="never"
|
||||||
|
@click="handleEdit(scope.row)"
|
||||||
|
style="margin-right: 8px"
|
||||||
|
>
|
||||||
|
<i class="iconfont-sys"></i> 修改
|
||||||
|
</el-link>
|
||||||
|
<!-- <el-link
|
||||||
|
v-if="hasAuth('receipt_records@destroy')"
|
||||||
|
type="danger"
|
||||||
|
underline="never"
|
||||||
|
@click="handleDelete()"
|
||||||
|
style="margin-right: 8px"
|
||||||
|
>
|
||||||
|
<i class="iconfont-sys"></i> 作废
|
||||||
|
</el-link> -->
|
||||||
|
<el-link
|
||||||
|
v-if="hasAuth('receipt_records@history')"
|
||||||
|
type="warning"
|
||||||
|
underline="never"
|
||||||
|
@click="handleHistory(scope.row)"
|
||||||
|
>
|
||||||
|
<i class="iconfont-sys"></i> 历史记录
|
||||||
|
</el-link>
|
||||||
|
</ElRow>
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
|
</template>
|
||||||
|
</ArtTable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新建/编辑收款记录 - 通用弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="isEdit ? '编辑收款记录' : '创建收款记录'"
|
||||||
|
width="900px"
|
||||||
|
class="receipt-dialog"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="createFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="createRules"
|
||||||
|
label-width="180px"
|
||||||
|
class="receipt-form"
|
||||||
|
>
|
||||||
|
<ElRow :gutter="12" class="item-row-wrap">
|
||||||
|
<!-- <ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="付款来源">
|
||||||
|
<el-select v-model="form.country_id" placeholder="请选择付款国家">
|
||||||
|
<el-option
|
||||||
|
v-for="item in countryOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.cn_addrs"
|
||||||
|
:value="item.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol> -->
|
||||||
|
<ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="汇款人">
|
||||||
|
<el-input v-model="form.remitter" placeholder="请填写汇款人" />
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="收款账户">
|
||||||
|
<el-select v-model="form.account_id" placeholder="请选择收款账户">
|
||||||
|
<el-option
|
||||||
|
v-for="item in accountOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.short_desc"
|
||||||
|
:value="item.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="币种">
|
||||||
|
<el-select v-model="form.currency_id" placeholder="请选择币种">
|
||||||
|
<el-option
|
||||||
|
v-for="item in currencyOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.currency_name"
|
||||||
|
:value="item.id"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="金额">
|
||||||
|
<el-input v-model="form.amount" placeholder="请填写金额" />
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="到账时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.arrived_at"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择到账时间"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
<!-- <ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="折让金额">
|
||||||
|
<el-input v-model="form.discount_amount" placeholder="请填写折让金额" />
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol> -->
|
||||||
|
<ElCol :sm="24" :md="12" :lg="12" v-if="isEdit">
|
||||||
|
<el-form-item label="收汇凭证号">
|
||||||
|
<el-input v-model="form.voucher_no" placeholder="请填写收汇凭证号" />
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
<!-- <ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="实际汇率(出纳)">
|
||||||
|
<el-input v-model="form.exchange_rate" placeholder="请填写实际汇率" />
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol> -->
|
||||||
|
<ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="总金额(人民币)实际汇率">
|
||||||
|
<el-input
|
||||||
|
v-model="form.total_amount_cny"
|
||||||
|
placeholder="请填写总金额(人民币)实际汇率"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :sm="24" :md="12" :lg="12">
|
||||||
|
<el-form-item label="是否转回内地">
|
||||||
|
<el-radio-group v-model="form.is_domestic_return">
|
||||||
|
<el-radio :label="0">否</el-radio>
|
||||||
|
<el-radio :label="1">是</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :sm="24" :md="24" :lg="24">
|
||||||
|
<el-form-item label="外汇到账回单(出纳)">
|
||||||
|
<el-upload
|
||||||
|
class="upload-demo"
|
||||||
|
:action="actionImageUplode"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:on-success="handleUploadSuccess1"
|
||||||
|
:show-file-list="false"
|
||||||
|
:multiple="true"
|
||||||
|
>
|
||||||
|
<img :src="imageUrl1" alt="" class="avatarimg" />
|
||||||
|
<input type="hidden" v-model="form.fx_receipt_file" />
|
||||||
|
</el-upload>
|
||||||
|
<div
|
||||||
|
v-if="Array.isArray(form.fx_receipt_file) && form.fx_receipt_file.length"
|
||||||
|
style="margin-top: 8px"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(u, i) in form.fx_receipt_file"
|
||||||
|
:key="i"
|
||||||
|
style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="
|
||||||
|
/\.(png|jpe?g|gif|webp|bmp|svg)(\?|$)/i.test(
|
||||||
|
(u.file_full || '').split('?')[0]
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:src="u.file_full"
|
||||||
|
style="
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
v-else
|
||||||
|
:href="u.file_full"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
style="
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>{{ u.file_name }}</a
|
||||||
|
>
|
||||||
|
<el-button link type="danger" @click="removeVoucher(i)" style="width: 80px"
|
||||||
|
>移除</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</ElCol>
|
||||||
|
</ElRow>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="handleSubmit">提交</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisibleshow"
|
||||||
|
title="查看收款记录"
|
||||||
|
width="900px"
|
||||||
|
class="receipt-dialog"
|
||||||
|
>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<label>创建日期:</label>
|
||||||
|
<span>{{ formshow.created_at }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="info-item">
|
||||||
|
<label>付款来源:</label>
|
||||||
|
<span>{{ formshow.country_name || '—' }}</span>
|
||||||
|
</div> -->
|
||||||
|
<div class="info-item">
|
||||||
|
<label>汇款人:</label>
|
||||||
|
<span>{{ formshow.remitter || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>账户:</label>
|
||||||
|
<span>{{ formshow.account_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>币种:</label>
|
||||||
|
<span>{{ formshow.currency_name || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>金额:</label>
|
||||||
|
<span class="amount">{{ formshow.amount }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>到账时间:</label>
|
||||||
|
<span>{{ formshow.arrived_at || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="info-item">
|
||||||
|
<label>折让金额:</label>
|
||||||
|
<span>{{ formshow.discount_amount || '—' }}</span>
|
||||||
|
</div> -->
|
||||||
|
<div class="info-item">
|
||||||
|
<label>收汇凭证号:</label>
|
||||||
|
<span>{{ formshow.voucher_no || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="info-item">
|
||||||
|
<label>实际汇率(出纳):</label>
|
||||||
|
<span>{{ formshow.exchange_rate || '—' }}</span>
|
||||||
|
</div> -->
|
||||||
|
<div class="info-item">
|
||||||
|
<label>总金额(人民币)实际汇率:</label>
|
||||||
|
<span>{{ formshow.total_amount_cny || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>是否转回内地:</label>
|
||||||
|
<span>{{ formshow.is_domestic_return == 1 ? '是' : '否' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<label>外汇到账回单:</label>
|
||||||
|
<div>
|
||||||
|
<p v-for="(item, index) in formshow.fx_receipt_file" :key="index">
|
||||||
|
<el-image
|
||||||
|
v-if="
|
||||||
|
/\.(png|jpe?g|gif|webp|bmp|svg)(\?|$)/i.test((item.file_full || '').split('?')[0])
|
||||||
|
"
|
||||||
|
:src="item.file_full"
|
||||||
|
:preview-src-list="item.file_full ? [item.file_full] : []"
|
||||||
|
style="width: 100px; height: 100px"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
v-else
|
||||||
|
:href="item.file_full"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
style="
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>{{ item.file_name }}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
<!-- 历史记录 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="timelineDialogVisible"
|
||||||
|
title="历史记录"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
class="approval-dialog"
|
||||||
|
>
|
||||||
|
<div class="approval-content">
|
||||||
|
<!-- 头部信息 -->
|
||||||
|
<Timeline :records="approvalRecords" />
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { commonService } from '@/api/commonApi'
|
||||||
|
import { financeService } from '@/api/admin/finance'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import { useAuth } from '@/composables/useAuth'
|
||||||
|
import imageUrl1 from '@/assets/img/crm/upimg.png'
|
||||||
|
import { useUploadHeaders } from '@/composables/useUploadHeaders'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
const Timeline = defineAsyncComponent(() => import('@/components/custom/Timeline.vue'))
|
||||||
|
const { uploadHeaders } = useUploadHeaders()
|
||||||
|
let actionImageUplode = `${import.meta.env.VITE_API_URL}/ticket/upload?scene=receipt_records`
|
||||||
|
defineOptions({ name: 'AdminReceiptRecords' })
|
||||||
|
const approvalRecords = ref<any[]>([])
|
||||||
|
const { hasAuth } = useAuth()
|
||||||
|
const loading = ref(false)
|
||||||
|
const dialogVisibleshow = ref(false)
|
||||||
|
const timelineDialogVisible = ref(false)
|
||||||
|
const keyword = ref('')
|
||||||
|
const tableRef = ref()
|
||||||
|
const tableData = ref<any[]>([])
|
||||||
|
const pagination = reactive({ currentPage: 1, pageSize: 10, total: 0 })
|
||||||
|
//国家
|
||||||
|
const countryOptions = ref<any[]>([])
|
||||||
|
const fetchCountryOptions = async () => {
|
||||||
|
try {
|
||||||
|
const res = await commonService.getCountrySelect()
|
||||||
|
res.forEach((item: any) => {
|
||||||
|
item.cn_addrs = item.cn_addr + '(' + item.en_addr + ')'
|
||||||
|
})
|
||||||
|
countryOptions.value = res || []
|
||||||
|
} catch {
|
||||||
|
countryOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//账户
|
||||||
|
const accountOptions = ref<any[]>([])
|
||||||
|
const fetchAccountOptions = async () => {
|
||||||
|
try {
|
||||||
|
const res = await commonService.getAccountSelect()
|
||||||
|
accountOptions.value = res || []
|
||||||
|
} catch {
|
||||||
|
accountOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//币种
|
||||||
|
const currencyOptions = ref<any[]>([])
|
||||||
|
const fetchCurrencyOptions = async () => {
|
||||||
|
try {
|
||||||
|
const res = await commonService.getCurrencySelect()
|
||||||
|
currencyOptions.value = res || []
|
||||||
|
} catch {
|
||||||
|
currencyOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审批弹出框相关
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch([keyword], () => {
|
||||||
|
pagination.currentPage = 1
|
||||||
|
getList()
|
||||||
|
// 不自动请求,保持与截图行为一致,点击搜索或回车触发
|
||||||
|
})
|
||||||
|
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const { currentPage, pageSize } = pagination
|
||||||
|
const params = {
|
||||||
|
current: currentPage,
|
||||||
|
size: pageSize,
|
||||||
|
keyword: keyword.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const { records, current, size, total } = await financeService.getReceiptList(params)
|
||||||
|
tableData.value = records || []
|
||||||
|
Object.assign(pagination, { currentPage: current, pageSize: size, total })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取列表失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tableRowClassName = ({ row }: { row: any }) => {
|
||||||
|
const flag = Number(row?.claim_timeout)
|
||||||
|
return flag === 1 ? 'row-claim-timeout' : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSizeChange = (size: number) => {
|
||||||
|
pagination.pageSize = size
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
const handleCurrentChange = (page: number) => {
|
||||||
|
pagination.currentPage = page
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建/编辑弹窗通用状态
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const createLoading = ref(false)
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const currentId = ref<string | number | null>(null)
|
||||||
|
const createFormRef = ref<FormInstance>()
|
||||||
|
interface FileInfo {
|
||||||
|
file_name?: string
|
||||||
|
file_full?: string
|
||||||
|
file_src?: string
|
||||||
|
}
|
||||||
|
const form = ref({
|
||||||
|
remitter: '',
|
||||||
|
account_id: '',
|
||||||
|
currency_id: '',
|
||||||
|
amount: '',
|
||||||
|
arrived_at: new Date().toISOString().split('T')[0],
|
||||||
|
voucher_no: '',
|
||||||
|
total_amount_cny: '',
|
||||||
|
is_domestic_return: 0,
|
||||||
|
fx_receipt_file: [] as FileInfo[],
|
||||||
|
fx_receipt_file_list: [] as object[]
|
||||||
|
})
|
||||||
|
const createRules: FormRules = {
|
||||||
|
remitter: [{ required: true, message: '请输入汇款人', trigger: 'blur' }],
|
||||||
|
account_id: [{ required: true, message: '请选择账户', trigger: 'change' }],
|
||||||
|
currency_id: [{ required: true, message: '请选择币种', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
const formshow = ref({
|
||||||
|
created_at: '',
|
||||||
|
country_name: '',
|
||||||
|
remitter: '',
|
||||||
|
account_name: '',
|
||||||
|
currency_name: '',
|
||||||
|
amount: '',
|
||||||
|
arrived_at: '',
|
||||||
|
voucher_no: '',
|
||||||
|
total_amount_cny: '',
|
||||||
|
is_domestic_return: 0,
|
||||||
|
fx_receipt_file: [] as FileInfo[],
|
||||||
|
fx_receipt_file_full: '',
|
||||||
|
fx_receipt_file_list: [] as object[]
|
||||||
|
})
|
||||||
|
const resetCreateForm = () => {
|
||||||
|
Object.assign(form.value, {
|
||||||
|
remitter: '',
|
||||||
|
account_id: '',
|
||||||
|
currency_id: '',
|
||||||
|
amount: '',
|
||||||
|
arrived_at: new Date().toISOString().split('T')[0],
|
||||||
|
voucher_no: '',
|
||||||
|
total_amount_cny: '',
|
||||||
|
is_domestic_return: 0,
|
||||||
|
fx_receipt_file: [] as FileInfo[],
|
||||||
|
fx_receipt_file_list: []
|
||||||
|
})
|
||||||
|
currentId.value = null
|
||||||
|
createFormRef.value?.clearValidate?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreate = async () => {
|
||||||
|
resetCreateForm()
|
||||||
|
isEdit.value = false
|
||||||
|
fetchAccountOptions()
|
||||||
|
fetchCurrencyOptions()
|
||||||
|
fetchCountryOptions()
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
createFormRef.value?.validate?.(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
try {
|
||||||
|
createLoading.value = true
|
||||||
|
// TODO: 根据新增/编辑调用对应接口
|
||||||
|
if (isEdit.value) {
|
||||||
|
const formData = { ...form.value }
|
||||||
|
// if ('fx_receipt_file_list' in formData) {
|
||||||
|
// delete formData.fx_receipt_file_list
|
||||||
|
// }
|
||||||
|
const forms = { ...formData, id: currentId.value }
|
||||||
|
const { message } = await financeService.updateReceipt({
|
||||||
|
remitter: forms.remitter,
|
||||||
|
account_id: forms.account_id,
|
||||||
|
currency_id: forms.currency_id,
|
||||||
|
amount: forms.amount,
|
||||||
|
arrived_at: forms.arrived_at,
|
||||||
|
voucher_no: forms.voucher_no,
|
||||||
|
total_amount_cny: forms.total_amount_cny,
|
||||||
|
is_domestic_return: forms.is_domestic_return,
|
||||||
|
fx_receipt_file: forms.fx_receipt_file,
|
||||||
|
id: forms.id
|
||||||
|
})
|
||||||
|
ElMessage.success(message)
|
||||||
|
} else {
|
||||||
|
const formData = { ...form.value }
|
||||||
|
const { message } = await financeService.addReceipt({
|
||||||
|
remitter: formData.remitter,
|
||||||
|
account_id: formData.account_id,
|
||||||
|
currency_id: formData.currency_id,
|
||||||
|
amount: formData.amount,
|
||||||
|
arrived_at: formData.arrived_at,
|
||||||
|
voucher_no: formData.voucher_no,
|
||||||
|
total_amount_cny: formData.total_amount_cny,
|
||||||
|
is_domestic_return: formData.is_domestic_return,
|
||||||
|
fx_receipt_file: formData.fx_receipt_file
|
||||||
|
})
|
||||||
|
ElMessage.success(message)
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
getList()
|
||||||
|
} finally {
|
||||||
|
createLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const handleView = (row?: any) => {
|
||||||
|
// const fx_receipt_file_full = []
|
||||||
|
// if (row.fx_receipt_file_full) {
|
||||||
|
// fx_receipt_file_full.push(row.fx_receipt_file_full)
|
||||||
|
// }
|
||||||
|
formshow.value = row
|
||||||
|
// formshow.value.fx_receipt_file_list = fx_receipt_file_full
|
||||||
|
dialogVisibleshow.value = true
|
||||||
|
}
|
||||||
|
const handleEdit = (row?: any) => {
|
||||||
|
resetCreateForm()
|
||||||
|
isEdit.value = true
|
||||||
|
currentId.value = row?.id ?? null
|
||||||
|
if (row && typeof row === 'object') {
|
||||||
|
// form.value.country_id = row.country_id ?? form.value.country_id
|
||||||
|
form.value.remitter = row.remitter ?? form.value.remitter
|
||||||
|
form.value.account_id = row.account_id ?? form.value.account_id
|
||||||
|
form.value.currency_id = row.currency_id ?? form.value.currency_id
|
||||||
|
form.value.amount = row.amount ?? form.value.amount
|
||||||
|
form.value.arrived_at = row.arrived_at ?? form.value.arrived_at
|
||||||
|
// form.value.discount_amount = row.discount_amount ?? form.value.discount_amount
|
||||||
|
form.value.voucher_no = row.voucher_no ?? form.value.voucher_no
|
||||||
|
form.value.total_amount_cny = row.total_amount_cny ?? form.value.total_amount_cny
|
||||||
|
// form.value.exchange_rate = row.exchange_rate ?? form.value.exchange_rate
|
||||||
|
form.value.is_domestic_return = row.is_domestic_return ?? form.value.is_domestic_return
|
||||||
|
form.value.fx_receipt_file = row.fx_receipt_file ?? form.value.fx_receipt_file
|
||||||
|
form.value.fx_receipt_file_list = row.fx_receipt_file ?? form.value.fx_receipt_file
|
||||||
|
|
||||||
|
// const fx_receipt_file_full = []
|
||||||
|
// if (row.fx_receipt_file_full) {
|
||||||
|
// fx_receipt_file_full.push(row.fx_receipt_file_full)
|
||||||
|
// }
|
||||||
|
// form.value.fx_receipt_file_list = Array.isArray(fx_receipt_file_full)
|
||||||
|
// ? fx_receipt_file_full
|
||||||
|
// : form.value.fx_receipt_file_list
|
||||||
|
}
|
||||||
|
fetchAccountOptions()
|
||||||
|
fetchCurrencyOptions()
|
||||||
|
fetchCountryOptions()
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
const handlebtn = (row?: any) => {
|
||||||
|
ElMessageBox.prompt('你是否确认入账?确认入账请填写入账实时汇率!', '入账', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
inputPattern: /^(?!\s*$).+/,
|
||||||
|
inputErrorMessage: '请填写入账实时汇率'
|
||||||
|
}).then(({ value }) => {
|
||||||
|
console.log(value)
|
||||||
|
financeService.confirmReceipt({ id: row.id, exchange_rate: value }).then((res) => {
|
||||||
|
ElMessage.success(res.message)
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// const handleDelete = () => {
|
||||||
|
// ElMessageBox.confirm('确定删除吗?', '提示', {
|
||||||
|
// confirmButtonText: '确定',
|
||||||
|
// cancelButtonText: '取消',
|
||||||
|
// type: 'warning'
|
||||||
|
// }).then(() => {
|
||||||
|
// financeService.deleteReceipt({ id: currentId.value }).then((res) => {
|
||||||
|
// ElMessage.success(res.message)
|
||||||
|
// getList()
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
const handleHistory = (row: any) => {
|
||||||
|
financeService.getReceiptHistory({ receipt_id: row.id }).then((res) => {
|
||||||
|
timelineDialogVisible.value = true
|
||||||
|
approvalRecords.value = res.records || []
|
||||||
|
// ElMessage.success(res.message)
|
||||||
|
// getList()
|
||||||
|
})
|
||||||
|
|
||||||
|
// history.push('/admin/finance/receipt_records_history')
|
||||||
|
}
|
||||||
|
// function handleUploadSuccess1(response: any) {
|
||||||
|
// const fileName = response?.data?.file_name || ''
|
||||||
|
// const fileUrl = response?.data?.file_url || response?.data?.full_url || response?.url || ''
|
||||||
|
// form.value.fx_receipt_file = [form.value.fx_receipt_file, fileName].filter(Boolean).join(',')
|
||||||
|
// const list = Array.isArray(form.value.fx_receipt_file_list)
|
||||||
|
// ? form.value.fx_receipt_file_list
|
||||||
|
// : []
|
||||||
|
// list.push(fileUrl)
|
||||||
|
// form.value.fx_receipt_file_list = [...list]
|
||||||
|
// ElMessage.success(response.message)
|
||||||
|
// }
|
||||||
|
function handleUploadSuccess1(response: any) {
|
||||||
|
const fileName = response?.data?.file_name || ''
|
||||||
|
const fileUrl = response?.data?.file_url || response?.data?.full_url || response?.url || ''
|
||||||
|
const names = Array.isArray(form.value.fx_receipt_file) ? [...form.value.fx_receipt_file] : []
|
||||||
|
const files = ref({
|
||||||
|
file_src: '',
|
||||||
|
file_name: '',
|
||||||
|
file_full: ''
|
||||||
|
})
|
||||||
|
files.value.file_src = fileName
|
||||||
|
files.value.file_name = response?.data?.original_name || ''
|
||||||
|
files.value.file_full = response?.data?.file_url || ''
|
||||||
|
if (fileName) names.push(files.value)
|
||||||
|
form.value.fx_receipt_file = names
|
||||||
|
const list = Array.isArray(form.value.fx_receipt_file_list)
|
||||||
|
? form.value.fx_receipt_file_list
|
||||||
|
: []
|
||||||
|
list.push(fileUrl)
|
||||||
|
form.value.fx_receipt_file_list = [...list]
|
||||||
|
ElMessage.success(response.message)
|
||||||
|
}
|
||||||
|
// 预览项移除(同步预览数组与提交字段)
|
||||||
|
function removeVoucher(index: number) {
|
||||||
|
// const list = Array.isArray(form.value.fx_receipt_file_list)
|
||||||
|
// ? [...form.value.fx_receipt_file_list]
|
||||||
|
// : []
|
||||||
|
// if (index < 0 || index >= list.length) return
|
||||||
|
// list.splice(index, 1)
|
||||||
|
// form.value.fx_receipt_file_list = list
|
||||||
|
// const names = (form.value.fx_receipt_file || '').split(',').filter(Boolean)
|
||||||
|
// if (index < names.length) names.splice(index, 1)
|
||||||
|
// form.value.fx_receipt_file = names.join(',')
|
||||||
|
|
||||||
|
const list = Array.isArray(form.value.fx_receipt_file_list)
|
||||||
|
? [...form.value.fx_receipt_file_list]
|
||||||
|
: []
|
||||||
|
if (index < 0 || index >= list.length) return
|
||||||
|
list.splice(index, 1)
|
||||||
|
form.value.fx_receipt_file_list = list
|
||||||
|
const names = Array.isArray(form.value.fx_receipt_file) ? [...form.value.fx_receipt_file] : []
|
||||||
|
if (index < names.length) names.splice(index, 1)
|
||||||
|
form.value.fx_receipt_file = names
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
:deep(.row-claim-timeout) {
|
||||||
|
background-color: #ffc9c9 !important;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.stocking-page {
|
||||||
|
background: var(--art-main-bg-color);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.stocking-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--art-main-bg-color);
|
||||||
|
}
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.toolbar-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
// 创建收款记录 - 弹窗样式对齐设计图
|
||||||
|
.receipt-dialog {
|
||||||
|
.receipt-form {
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-demo {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: flex-end;
|
||||||
|
// border: 1px dashed #dcdfe6;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.avatarimg {
|
||||||
|
width: 60px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
width: 200px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.info-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// padding: 8px 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666;
|
||||||
|
min-width: 100px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&.amount {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #3d7bff;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.receipt-dialog .el-dialog__footer) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -42,7 +42,12 @@
|
|||||||
|
|
||||||
<!-- 数据表格 -->
|
<!-- 数据表格 -->
|
||||||
<div class="table-container boxshadow">
|
<div class="table-container boxshadow">
|
||||||
<ElButton type="primary" v-ripple @click="handleAdd">新增车辆</ElButton>
|
<div class="btntab">
|
||||||
|
<ElButton type="primary" v-ripple @click="handleAdd">新增车辆</ElButton>
|
||||||
|
<ElButton type="success" v-ripple @click="crawlKeywordfn"
|
||||||
|
>通过车系名称查找抓取车辆</ElButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<ArtTable
|
<ArtTable
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
@@ -988,14 +993,76 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
<!-- 关键字 车型抓取弹出框 -->
|
||||||
|
<ElDialog v-model="keywordDialogVisible" title="关键字-车型采集" width="900px" align-center>
|
||||||
|
<div class="collect-models-container">
|
||||||
|
<div style="display: flex; gap: 12px">
|
||||||
|
<el-form-item label="关键字:" label-width="70px">
|
||||||
|
<el-input
|
||||||
|
v-model="carkeyword"
|
||||||
|
placeholder="请输入关键字"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="getcarkeyword"
|
||||||
|
>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="getcarkeyword" v-ripple>采集</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="collect-models-content">
|
||||||
|
<div v-if="modelLstLoading" class="loading-container">
|
||||||
|
<ElLoading text="正在采集车型数据..." />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!modelLst || modelLst.length === 0" class="empty-container">
|
||||||
|
<ElEmpty description="暂无车型数据" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="models-table-container">
|
||||||
|
<ElTable
|
||||||
|
:data="modelLst"
|
||||||
|
style="width: 100%"
|
||||||
|
max-height="400"
|
||||||
|
@selection-change="handleModelSelectionChange"
|
||||||
|
>
|
||||||
|
<ElTableColumn type="selection" width="55" />
|
||||||
|
<ElTableColumn prop="brand_name" label="品牌" width="100" />
|
||||||
|
<ElTableColumn prop="series_name" label="车系" width="120" />
|
||||||
|
<ElTableColumn prop="title" label="车型名称" min-width="200" show-overflow-tooltip />
|
||||||
|
<ElTableColumn prop="year" label="年款" width="80" />
|
||||||
|
<ElTableColumn prop="msrp" label="指导价" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.msrp" class="price-text">
|
||||||
|
¥{{ formatPrice(scope.row.msrp) }}
|
||||||
|
</span>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
|
</ElTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<ElButton @click="keywordclose">取消</ElButton>
|
||||||
|
<ElButton
|
||||||
|
type="primary"
|
||||||
|
@click="saveKeywordModels"
|
||||||
|
:disabled="selectedModels.length === 0"
|
||||||
|
>
|
||||||
|
保存选中车型 ({{ selectedModels.length }})
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ElDialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
|
||||||
import { vehicleService } from '@/api/vehicleApi'
|
import { vehicleService } from '@/api/vehicleApi'
|
||||||
import { commonService } from '@/api/commonApi'
|
import { commonService } from '@/api/commonApi'
|
||||||
defineOptions({ name: 'AdminVehicleList' })
|
defineOptions({ name: 'AdminVehicleList' })
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
keyword: '',
|
keyword: '',
|
||||||
@@ -1233,7 +1300,77 @@
|
|||||||
pagination.currentPage = page
|
pagination.currentPage = page
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
//抓取车辆
|
||||||
|
const keywordDialogVisible = ref(false)
|
||||||
|
const carkeyword = ref('')
|
||||||
|
const keywordModelsData = ref<any>({})
|
||||||
|
const modelLst = ref<any>([])
|
||||||
|
const modelLstLoading = ref(false)
|
||||||
|
const crawlKeywordfn = async () => {
|
||||||
|
keywordDialogVisible.value = true
|
||||||
|
carkeyword.value = ''
|
||||||
|
keywordModelsData.value = {}
|
||||||
|
modelLst.value = []
|
||||||
|
}
|
||||||
|
const keywordclose = () => {
|
||||||
|
keywordDialogVisible.value = false
|
||||||
|
carkeyword.value = ''
|
||||||
|
keywordModelsData.value = {}
|
||||||
|
modelLst.value = []
|
||||||
|
}
|
||||||
|
const handleModelSelectionChange = (selection: any[]) => {
|
||||||
|
selectedModels.value = selection
|
||||||
|
}
|
||||||
|
const selectedModels = ref<any[]>([])
|
||||||
|
const getcarkeyword = async () => {
|
||||||
|
modelLstLoading.value = true
|
||||||
|
selectedModels.value = []
|
||||||
|
const loading = ElLoading.service({
|
||||||
|
lock: true,
|
||||||
|
text: 'Loading',
|
||||||
|
background: 'rgba(0, 0, 0, 0.7)'
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const res = await commonService.getcrawlKeyword({ keyword: carkeyword.value })
|
||||||
|
keywordModelsData.value = res
|
||||||
|
modelLst.value = res.model
|
||||||
|
console.log(modelLst.value)
|
||||||
|
} finally {
|
||||||
|
loading.close()
|
||||||
|
modelLstLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const saveKeywordModels = async () => {
|
||||||
|
if (selectedModels.value.length === 0) {
|
||||||
|
ElMessage.warning('请选择要保存的车型')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await commonService.crawlModelCreate({
|
||||||
|
series_id: keywordModelsData.value.series.id ? keywordModelsData.value.series.id : 0,
|
||||||
|
brand_id: keywordModelsData.value.brand.id ? keywordModelsData.value.brand.id : 0,
|
||||||
|
models: selectedModels.value
|
||||||
|
})
|
||||||
|
ElMessage.success(`成功保存 ${selectedModels.value.length} 个车型`)
|
||||||
|
keywordDialogVisible.value = false
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
Object.keys(formData).forEach((key) => {
|
||||||
|
if (key === 'is_active') {
|
||||||
|
;(formData as any)[key] = 1
|
||||||
|
} else if (key === 'currency') {
|
||||||
|
;(formData as any)[key] = 'CNY'
|
||||||
|
} else {
|
||||||
|
;(formData as any)[key] = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
formRef.value?.clearValidate()
|
||||||
|
// 重置选项数据
|
||||||
|
modelsOptions.value = []
|
||||||
|
seriesOptions.value = []
|
||||||
|
brandOptions.value = []
|
||||||
|
}
|
||||||
// 新增车辆
|
// 新增车辆
|
||||||
const handleAdd = async () => {
|
const handleAdd = async () => {
|
||||||
dialogTitle.value = '新增车辆'
|
dialogTitle.value = '新增车辆'
|
||||||
@@ -1249,26 +1386,20 @@
|
|||||||
loadColorOptions()
|
loadColorOptions()
|
||||||
loadDictOptions()
|
loadDictOptions()
|
||||||
dialogTitle.value = '编辑车辆'
|
dialogTitle.value = '编辑车辆'
|
||||||
|
|
||||||
// 先清空表单和选项数据,避免监听器干扰
|
// 先清空表单和选项数据,避免监听器干扰
|
||||||
resetForm()
|
resetForm()
|
||||||
|
|
||||||
// 使用 nextTick 确保 DOM 更新后再设置数据
|
// 使用 nextTick 确保 DOM 更新后再设置数据
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// 设置表单数据
|
// 设置表单数据
|
||||||
Object.assign(formData, row)
|
Object.assign(formData, row)
|
||||||
|
|
||||||
// 立即检查车系名称是否被清空并重新设置
|
// 立即检查车系名称是否被清空并重新设置
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!formData.series_name) {
|
if (!formData.series_name) {
|
||||||
formData.series_name = row.series_name
|
formData.series_name = row.series_name
|
||||||
}
|
}
|
||||||
}, 1)
|
}, 1)
|
||||||
|
|
||||||
// 先加载品牌选项
|
// 先加载品牌选项
|
||||||
await loadBrandOptions()
|
await loadBrandOptions()
|
||||||
|
|
||||||
// 如果品牌已选择,加载车系选项
|
// 如果品牌已选择,加载车系选项
|
||||||
if (row.brand_code) {
|
if (row.brand_code) {
|
||||||
await loadSeriesOptions(row.brand_code)
|
await loadSeriesOptions(row.brand_code)
|
||||||
@@ -1281,7 +1412,6 @@
|
|||||||
await loadSeriesOptions(matchedBrand.brand_code)
|
await loadSeriesOptions(matchedBrand.brand_code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果车系已选择,加载车型选项
|
// 如果车系已选择,加载车型选项
|
||||||
if (row.series_code) {
|
if (row.series_code) {
|
||||||
// 先尝试用原始的车系代码加载
|
// 先尝试用原始的车系代码加载
|
||||||
@@ -1305,7 +1435,6 @@
|
|||||||
await loadModelsOptions(matchedSeries.series_code)
|
await loadModelsOptions(matchedSeries.series_code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果仍然没有车型选项,但有车系信息,尝试再次加载
|
// 如果仍然没有车型选项,但有车系信息,尝试再次加载
|
||||||
if (modelsOptions.value.length === 0 && (row.series_code || row.series_name)) {
|
if (modelsOptions.value.length === 0 && (row.series_code || row.series_name)) {
|
||||||
// 使用当前表单中的车系代码重新加载
|
// 使用当前表单中的车系代码重新加载
|
||||||
@@ -1313,7 +1442,6 @@
|
|||||||
await loadModelsOptions(formData.series_code)
|
await loadModelsOptions(formData.series_code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保车系代码与车系名称匹配(在选项数据加载完成后)
|
// 确保车系代码与车系名称匹配(在选项数据加载完成后)
|
||||||
// 如果车系名称被清空,重新设置
|
// 如果车系名称被清空,重新设置
|
||||||
if (!formData.series_name && row.series_name) {
|
if (!formData.series_name && row.series_name) {
|
||||||
@@ -1397,25 +1525,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置表单
|
|
||||||
const resetForm = () => {
|
|
||||||
Object.keys(formData).forEach((key) => {
|
|
||||||
if (key === 'is_active') {
|
|
||||||
;(formData as any)[key] = 1
|
|
||||||
} else if (key === 'currency') {
|
|
||||||
;(formData as any)[key] = 'CNY'
|
|
||||||
} else {
|
|
||||||
;(formData as any)[key] = ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
formRef.value?.clearValidate()
|
|
||||||
|
|
||||||
// 重置选项数据
|
|
||||||
modelsOptions.value = []
|
|
||||||
seriesOptions.value = []
|
|
||||||
brandOptions.value = []
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -1456,18 +1565,15 @@
|
|||||||
ElMessage.warning('请先选择车系')
|
ElMessage.warning('请先选择车系')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const matched = seriesOptions.value.find((s: any) => s.series_code === formData.series_code)
|
const matched = seriesOptions.value.find((s: any) => s.series_code === formData.series_code)
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
ElMessage.warning('未找到对应的车系信息')
|
ElMessage.warning('未找到对应的车系信息')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
currentCollectSeries.value = matched
|
currentCollectSeries.value = matched
|
||||||
collectModelsDialogVisible.value = true
|
collectModelsDialogVisible.value = true
|
||||||
collectModelsData.value = []
|
collectModelsData.value = []
|
||||||
selectedModelId.value = ''
|
selectedModelId.value = ''
|
||||||
|
|
||||||
// 调用车型采集
|
// 调用车型采集
|
||||||
collectVehicleModels(matched)
|
collectVehicleModels(matched)
|
||||||
}
|
}
|
||||||
@@ -1475,10 +1581,8 @@
|
|||||||
const collectVehicleModels = async (series: any) => {
|
const collectVehicleModels = async (series: any) => {
|
||||||
try {
|
try {
|
||||||
collectModelsLoading.value = true
|
collectModelsLoading.value = true
|
||||||
|
|
||||||
// 调用后端接口获取车型数据
|
// 调用后端接口获取车型数据
|
||||||
const response = await vehicleService.collectVehicleModels({ brand_series_id: series.id })
|
const response = await vehicleService.collectVehicleModels({ brand_series_id: series.id })
|
||||||
|
|
||||||
// 后端返回的数据格式为数组
|
// 后端返回的数据格式为数组
|
||||||
collectModelsData.value = response || []
|
collectModelsData.value = response || []
|
||||||
selectedModelId.value = ''
|
selectedModelId.value = ''
|
||||||
@@ -1582,7 +1686,6 @@
|
|||||||
() => formData.series_name,
|
() => formData.series_name,
|
||||||
(newSeriesName, oldSeriesName) => {
|
(newSeriesName, oldSeriesName) => {
|
||||||
if (!newSeriesName) return
|
if (!newSeriesName) return
|
||||||
|
|
||||||
const matchedSeries = seriesOptions.value.find(
|
const matchedSeries = seriesOptions.value.find(
|
||||||
(s: any) => s.series_name === newSeriesName || s.name === newSeriesName
|
(s: any) => s.series_name === newSeriesName || s.name === newSeriesName
|
||||||
)
|
)
|
||||||
@@ -1801,6 +1904,10 @@
|
|||||||
.table-container {
|
.table-container {
|
||||||
background: var(--art-main-bg-color);
|
background: var(--art-main-bg-color);
|
||||||
}
|
}
|
||||||
|
.btntab {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
// 表单样式
|
// 表单样式
|
||||||
.vehicle-form {
|
.vehicle-form {
|
||||||
|
|||||||
@@ -42,7 +42,12 @@
|
|||||||
|
|
||||||
<!-- 数据表格 -->
|
<!-- 数据表格 -->
|
||||||
<div class="table-container boxshadow">
|
<div class="table-container boxshadow">
|
||||||
<ElButton type="primary" v-ripple @click="handleAdd">新增车辆</ElButton>
|
<div class="btntab">
|
||||||
|
<ElButton type="primary" v-ripple @click="handleAdd">新增车辆</ElButton>
|
||||||
|
<ElButton type="success" v-ripple @click="crawlKeywordfn"
|
||||||
|
>通过车系名称查找抓取车辆</ElButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
<ArtTable
|
<ArtTable
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
@@ -988,11 +993,72 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
<!-- 关键字 车型抓取弹出框 -->
|
||||||
|
<ElDialog v-model="keywordDialogVisible" title="关键字-车型采集" width="900px" align-center>
|
||||||
|
<div class="collect-models-container">
|
||||||
|
<div style="display: flex; gap: 12px">
|
||||||
|
<el-form-item label="关键字:" label-width="70px">
|
||||||
|
<el-input
|
||||||
|
v-model="carkeyword"
|
||||||
|
placeholder="请输入关键字"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="getcarkeyword"
|
||||||
|
>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="getcarkeyword" v-ripple>采集</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="collect-models-content">
|
||||||
|
<div v-if="modelLstLoading" class="loading-container">
|
||||||
|
<ElLoading text="正在采集车型数据..." />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!modelLst || modelLst.length === 0" class="empty-container">
|
||||||
|
<ElEmpty description="暂无车型数据" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="models-table-container">
|
||||||
|
<ElTable
|
||||||
|
:data="modelLst"
|
||||||
|
style="width: 100%"
|
||||||
|
max-height="400"
|
||||||
|
@selection-change="handleModelSelectionChange"
|
||||||
|
>
|
||||||
|
<ElTableColumn type="selection" width="55" />
|
||||||
|
<ElTableColumn prop="brand_name" label="品牌" width="100" />
|
||||||
|
<ElTableColumn prop="series_name" label="车系" width="120" />
|
||||||
|
<ElTableColumn prop="title" label="车型名称" min-width="200" show-overflow-tooltip />
|
||||||
|
<ElTableColumn prop="year" label="年款" width="80" />
|
||||||
|
<ElTableColumn prop="msrp" label="指导价" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.msrp" class="price-text">
|
||||||
|
¥{{ formatPrice(scope.row.msrp) }}
|
||||||
|
</span>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
|
</ElTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<ElButton @click="keywordclose">取消</ElButton>
|
||||||
|
<ElButton
|
||||||
|
type="primary"
|
||||||
|
@click="saveKeywordModels"
|
||||||
|
:disabled="selectedModels.length === 0"
|
||||||
|
>
|
||||||
|
保存选中车型 ({{ selectedModels.length }})
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ElDialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
|
||||||
import { vehicleService } from '@/api/crmpc/vehicleApi'
|
import { vehicleService } from '@/api/crmpc/vehicleApi'
|
||||||
import { commonService } from '@/api/commonApi'
|
import { commonService } from '@/api/commonApi'
|
||||||
defineOptions({ name: 'AdminVehicleList' })
|
defineOptions({ name: 'AdminVehicleList' })
|
||||||
@@ -1766,9 +1832,68 @@
|
|||||||
formData.emission_std_code = matchedEmissionStd ? matchedEmissionStd.item_value : ''
|
formData.emission_std_code = matchedEmissionStd ? matchedEmissionStd.item_value : ''
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//抓取车辆
|
||||||
|
const keywordDialogVisible = ref(false)
|
||||||
|
const carkeyword = ref('')
|
||||||
|
const keywordModelsData = ref<any>({})
|
||||||
|
const modelLst = ref<any>([])
|
||||||
|
const modelLstLoading = ref(false)
|
||||||
|
const crawlKeywordfn = async () => {
|
||||||
|
keywordDialogVisible.value = true
|
||||||
|
carkeyword.value = ''
|
||||||
|
keywordModelsData.value = {}
|
||||||
|
modelLst.value = []
|
||||||
|
}
|
||||||
|
const keywordclose = () => {
|
||||||
|
keywordDialogVisible.value = false
|
||||||
|
carkeyword.value = ''
|
||||||
|
keywordModelsData.value = {}
|
||||||
|
modelLst.value = []
|
||||||
|
}
|
||||||
|
const handleModelSelectionChange = (selection: any[]) => {
|
||||||
|
selectedModels.value = selection
|
||||||
|
}
|
||||||
|
const selectedModels = ref<any[]>([])
|
||||||
|
const getcarkeyword = async () => {
|
||||||
|
modelLstLoading.value = true
|
||||||
|
selectedModels.value = []
|
||||||
|
const loading = ElLoading.service({
|
||||||
|
lock: true,
|
||||||
|
text: 'Loading',
|
||||||
|
background: 'rgba(0, 0, 0, 0.7)'
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const res = await commonService.getcrawlKeyword({ keyword: carkeyword.value })
|
||||||
|
keywordModelsData.value = res
|
||||||
|
modelLst.value = res.model
|
||||||
|
console.log(modelLst.value)
|
||||||
|
} finally {
|
||||||
|
loading.close()
|
||||||
|
modelLstLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const saveKeywordModels = async () => {
|
||||||
|
if (selectedModels.value.length === 0) {
|
||||||
|
ElMessage.warning('请选择要保存的车型')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await commonService.crawlModelCreate({
|
||||||
|
series_id: keywordModelsData.value.series.id ? keywordModelsData.value.series.id : 0,
|
||||||
|
brand_id: keywordModelsData.value.brand.id ? keywordModelsData.value.brand.id : 0,
|
||||||
|
models: selectedModels.value
|
||||||
|
})
|
||||||
|
ElMessage.success(`成功保存 ${selectedModels.value.length} 个车型`)
|
||||||
|
keywordDialogVisible.value = false
|
||||||
|
getList()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.btntab {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
.vehicle-page {
|
.vehicle-page {
|
||||||
background: var(--art-main-bg-color);
|
background: var(--art-main-bg-color);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
|||||||
@@ -0,0 +1,609 @@
|
|||||||
|
<template>
|
||||||
|
<div class="office-administrator">
|
||||||
|
<!-- 页面标题 -->
|
||||||
|
<!-- <div class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-icon">
|
||||||
|
<el-icon size="20" color="#409EFF">
|
||||||
|
<DocumentChecked />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
<h1 class="page-title">XXXXXXXXX备货单需要审批</h1>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<!-- 主要内容区域 -->
|
||||||
|
<div class="main-content">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :xs="24" :sm="24" :md="16">
|
||||||
|
<!-- 统计卡片区域 -->
|
||||||
|
<div class="stats-cards">
|
||||||
|
<div class="stats-card approved">
|
||||||
|
<div class="card-number">{{ officeclerkcard.approved }}</div>
|
||||||
|
<div class="card-label">已审批</div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="@/assets/img/admin/office1.png" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-card unapproved">
|
||||||
|
<div class="card-number">{{ officeclerkcard.pending }}</div>
|
||||||
|
<div class="card-label">未审批</div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="@/assets/img/admin/office2.png" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-card claimed">
|
||||||
|
<div class="card-number">{{ officeclerkcard.claimed }}</div>
|
||||||
|
<div class="card-label">已认领</div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="@/assets/img/admin/office3.png" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-card unclaimed">
|
||||||
|
<div class="card-number">{{ officeclerkcard.unclaimed }}</div>
|
||||||
|
<div class="card-label">未认领</div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="@/assets/img/admin/office4.png" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-card overdue">
|
||||||
|
<div class="card-number">{{ officeclerkcard.timeout }}</div>
|
||||||
|
<div class="card-label">已超时</div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="@/assets/img/admin/office5.png" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">超时列表</h3>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<ArtTable
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="id"
|
||||||
|
:loading="loadingtimeout"
|
||||||
|
:data="tableDatatimeout"
|
||||||
|
:currentPage="paginationtimeout.currentPage"
|
||||||
|
:pageSize="paginationtimeout.pageSize"
|
||||||
|
:total="paginationtimeout.total"
|
||||||
|
@size-change="handleSizeChangetimeout"
|
||||||
|
@current-change="handleCurrentChangetimeout"
|
||||||
|
lass="customer-table"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<el-table-column prop="id" label="备货单号" min-width="140">
|
||||||
|
<template #default="scope">
|
||||||
|
<p
|
||||||
|
class="colors"
|
||||||
|
v-for="(item, index) in scope.row.order_no"
|
||||||
|
@click="goTo()"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ item }}</p
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="created_at" label="创建日期"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="内勤"></el-table-column>
|
||||||
|
<el-table-column prop="payment_entity" label="汇款人"></el-table-column>
|
||||||
|
<el-table-column prop="payment_account" label="客户付款账户"></el-table-column>
|
||||||
|
<el-table-column prop="currency_name" label="币种"></el-table-column>
|
||||||
|
<el-table-column prop="amount" label="金额"></el-table-column>
|
||||||
|
<!-- <el-table-column label="操作" min-width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="follow-action" @click="goAddFollow(scope.row)"> 跟进 </div>
|
||||||
|
</template>
|
||||||
|
</el-table-column> -->
|
||||||
|
</template>
|
||||||
|
</ArtTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<div class="right-column">
|
||||||
|
<!-- 未审批列表 -->
|
||||||
|
<div class="unapproved-list">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">未审批列表</h3>
|
||||||
|
</div>
|
||||||
|
<div class="list-container">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in unapprovedList"
|
||||||
|
:key="index"
|
||||||
|
class="list-item"
|
||||||
|
@click="goToApprove"
|
||||||
|
>
|
||||||
|
<div class="item-content">
|
||||||
|
<span class="item-text colors">{{ item.order_no }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-action">
|
||||||
|
<span class="item-date">{{ item.approval_created_at }}</span>
|
||||||
|
<!-- <el-link type="primary" @click="goToApprove(item)">去审批</el-link> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- -->
|
||||||
|
<div class="chart-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">备货单统计</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-container">
|
||||||
|
<div ref="chartRef" class="chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div class="list-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">未认领列表</h3>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<ArtTable
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="id"
|
||||||
|
:loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
:currentPage="pagination.currentPage"
|
||||||
|
:pageSize="pagination.pageSize"
|
||||||
|
:total="pagination.total"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
lass="customer-table"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<el-table-column prop="created_at" label="创建日期">
|
||||||
|
<template #default="scope">
|
||||||
|
<p class="colors" @click="goTo()"> {{ scope.row.created_at }}</p>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="payment_entity" label="汇款人"></el-table-column>
|
||||||
|
<el-table-column prop="payment_account" label="客户付款账户"></el-table-column>
|
||||||
|
<el-table-column prop="currency_name" label="币种"></el-table-column>
|
||||||
|
<el-table-column prop="amount" label="金额"></el-table-column>
|
||||||
|
<!-- <el-table-column label="操作" min-width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="follow-action" @click="goAddFollow(scope.row)">跟进</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column> -->
|
||||||
|
</template>
|
||||||
|
</ArtTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
// import { DocumentChecked, DocumentCopy, Search, Files } from '@element-plus/icons-vue'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import { dashboardService } from '@/api/admin/dashboard'
|
||||||
|
import { router } from '@/router'
|
||||||
|
const chartRef = ref<HTMLElement>()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const loadingtimeout = ref(false)
|
||||||
|
const pagination = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
const paginationtimeout = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const tableData = ref([])
|
||||||
|
const tableDatatimeout = ref([])
|
||||||
|
// 处理表格分页变化
|
||||||
|
const handleSizeChange = (newPageSize: number) => {
|
||||||
|
pagination.pageSize = newPageSize
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (newCurrentPage: number) => {
|
||||||
|
pagination.currentPage = newCurrentPage
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
// 处理表格分页变化
|
||||||
|
const handleSizeChangetimeout = (newPageSize: number) => {
|
||||||
|
paginationtimeout.pageSize = newPageSize
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChangetimeout = (newCurrentPage: number) => {
|
||||||
|
paginationtimeout.currentPage = newCurrentPage
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
// 未审批列表数据
|
||||||
|
const unapprovedList = ref(<any>[])
|
||||||
|
|
||||||
|
// 初始化图表
|
||||||
|
const initChart = (options: any) => {
|
||||||
|
if (!chartRef.value) return
|
||||||
|
|
||||||
|
const chart = echarts.init(chartRef.value)
|
||||||
|
const optionsData = options.value || {}
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category' as const,
|
||||||
|
data: optionsData.optionType,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value' as const,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
interval: 10,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#666'
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#f0f0f0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: optionsData.counts,
|
||||||
|
type: 'line' as const,
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 6,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#409EFF',
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#409EFF'
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(64, 158, 255, 0.3)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(64, 158, 255, 0.05)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.setOption(option as any)
|
||||||
|
|
||||||
|
// 响应式调整
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
chart.resize()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去审批
|
||||||
|
const goToApprove = () => {
|
||||||
|
// 跳转到审批页面 /
|
||||||
|
router.push({
|
||||||
|
path: '/admin/approval/stocking_auth'
|
||||||
|
})
|
||||||
|
// 这里可以跳转到审批页面
|
||||||
|
}
|
||||||
|
const goTo = () => {
|
||||||
|
// 跳转到审批页面 /
|
||||||
|
router.push({
|
||||||
|
path: '/admin/finance/payment_records'
|
||||||
|
})
|
||||||
|
// 这里可以跳转到审批页面
|
||||||
|
}
|
||||||
|
//卡片接口
|
||||||
|
const officeclerkcard = ref(<any>{})
|
||||||
|
const getCardData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await dashboardService.officeclerkcard()
|
||||||
|
officeclerkcard.value = response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取卡片数据失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//待审批
|
||||||
|
const getOfficeclerkpending = async () => {
|
||||||
|
const response = await dashboardService.officeclerkpending()
|
||||||
|
unapprovedList.value = response
|
||||||
|
}
|
||||||
|
//曲线图
|
||||||
|
const officeclerkchart = ref(<any>{})
|
||||||
|
const getOfficeclerkchart = async () => {
|
||||||
|
const response = await dashboardService.salestockingcard()
|
||||||
|
officeclerkchart.value = response
|
||||||
|
initChart(officeclerkchart)
|
||||||
|
}
|
||||||
|
//待认领
|
||||||
|
const getOfficeclerkunclaimed = async () => {
|
||||||
|
const response = await dashboardService.officeclerkunclaimed({ type: 'unclaimed' })
|
||||||
|
tableData.value = response
|
||||||
|
pagination.total = response.length
|
||||||
|
|
||||||
|
const timeout = await dashboardService.officeclerkunclaimed({ type: 'timeout' })
|
||||||
|
tableDatatimeout.value = timeout
|
||||||
|
paginationtimeout.total = timeout.length
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCardData()
|
||||||
|
getOfficeclerkpending()
|
||||||
|
getOfficeclerkchart()
|
||||||
|
getOfficeclerkunclaimed()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.colors {
|
||||||
|
color: #409eff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.office-administrator {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background: #f0f9ff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
margin: 20px 0;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
.approved {
|
||||||
|
background: #f7f9fd;
|
||||||
|
}
|
||||||
|
.unapproved {
|
||||||
|
background: #f3fcff;
|
||||||
|
}
|
||||||
|
.claimed {
|
||||||
|
background: #fcfaff;
|
||||||
|
}
|
||||||
|
.unclaimed {
|
||||||
|
background: #fff2ea;
|
||||||
|
}
|
||||||
|
.overdue {
|
||||||
|
background: #ffe6e6;
|
||||||
|
}
|
||||||
|
.stats-card {
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
// box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
text-align: left;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.card-number {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
&.approved .card-number {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
&.unapproved .card-number {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
text-align: right;
|
||||||
|
img {
|
||||||
|
width: 74px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
margin: -20px;
|
||||||
|
}
|
||||||
|
.list-section {
|
||||||
|
margin: 20px 0;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.chart-section {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
height: 380px;
|
||||||
|
overflow: scroll;
|
||||||
|
.chart-container {
|
||||||
|
height: 300px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right-column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
.unapproved-list {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
// box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
height: 152px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.item-text {
|
||||||
|
font-size: 14px;
|
||||||
|
// color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-action {
|
||||||
|
.item-date {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
.el-link {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.section-bar {
|
||||||
|
width: 4px;
|
||||||
|
height: 20px;
|
||||||
|
background: #409eff;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式设计
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.stats-cards {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.stats-cards {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-grid {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.stats-cards {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -0,0 +1,758 @@
|
|||||||
|
<template>
|
||||||
|
<div class="office-administrator">
|
||||||
|
<!-- 主要内容区域 -->
|
||||||
|
<div class="main-content">
|
||||||
|
<div class="top">
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :xs="24" :sm="24" :md="16">
|
||||||
|
<div class="list-sectiont">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">今日入账金额</h3>
|
||||||
|
</div>
|
||||||
|
<div class="stats-cards">
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :span="19">
|
||||||
|
<div class="card-l">
|
||||||
|
<el-row :gutter="15" justify="space-between">
|
||||||
|
<el-col :sm="8" :md="3">
|
||||||
|
<img src="@/assets/img/admin/total.png" alt="" />
|
||||||
|
</el-col>
|
||||||
|
<el-col
|
||||||
|
:sm="8"
|
||||||
|
:md="2"
|
||||||
|
v-for="(item, key) in statisticscard.received_amount"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<div class="card-number">{{ item.amount }}</div>
|
||||||
|
<div class="card-label">{{ item.currency_code }}</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="5">
|
||||||
|
<div class="card-r">
|
||||||
|
<div class="card-number">{{ statisticscard.received_amount_sum }}</div>
|
||||||
|
<div class="card-label">总入账金额(CNY)</div>
|
||||||
|
<div class="card-icon">
|
||||||
|
<img src="@/assets/img/admin/financeall.png" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<div class="data-summary">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">数据汇总</h3>
|
||||||
|
</div>
|
||||||
|
<div class="summary-grid">
|
||||||
|
<div class="summary-item claimed">
|
||||||
|
<div class="summary-label">已认领总数</div>
|
||||||
|
<div class="summary-number blue">{{ statisticscard.claimed_num }}</div>
|
||||||
|
<div class="summary-tag blue">实时</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item unclaimed">
|
||||||
|
<div class="summary-label">未认领总数</div>
|
||||||
|
<div class="summary-number purple">{{ statisticscard.unclaimed_num }}</div>
|
||||||
|
<div class="summary-tag purple">实时</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item overdue">
|
||||||
|
<div class="summary-label">已超时总数</div>
|
||||||
|
<div class="summary-number green">{{ statisticscard.timeout }}</div>
|
||||||
|
<div class="summary-tag green">实时</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item not-accounted">
|
||||||
|
<div class="summary-label">未入账总数</div>
|
||||||
|
<div class="summary-number red">{{ statisticscard.unreceived_num }}</div>
|
||||||
|
<div class="summary-tag red">实时</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :xs="24" :sm="24" :md="16">
|
||||||
|
<div class="list-sectiont">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">在途金额</h3>
|
||||||
|
</div>
|
||||||
|
<div class="ontheway">
|
||||||
|
<el-row :gutter="15" justify="space-between">
|
||||||
|
<el-col
|
||||||
|
:sm="8"
|
||||||
|
:md="2"
|
||||||
|
v-for="(item, key) in statisticscard.intransit_amount_amount"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<div class="card-number">{{ item.amount }}</div>
|
||||||
|
<div class="card-label">{{ item.currency_code }}</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :xs="24" :sm="24" :md="14">
|
||||||
|
<div class="list-sectiont">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">在途总金额(CNY)</h3>
|
||||||
|
</div>
|
||||||
|
<div class="ontheway-cny"> {{ statisticscard.intransit_amount_sum }} </div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="10">
|
||||||
|
<div class="list-sectiont">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">在途总数量</h3>
|
||||||
|
</div>
|
||||||
|
<div class="ontheway-num"> {{ statisticscard.intransit_count }} </div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<!-- 金额 -->
|
||||||
|
<div class="list-amount">
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<div class="amount">
|
||||||
|
<div class="title">已认领金额</div>
|
||||||
|
<div class="amount-grid">
|
||||||
|
<el-row :gutter="15" justify="space-between">
|
||||||
|
<el-col
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
:md="2"
|
||||||
|
v-for="(item, key) in statisticscard.claimed_amount"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<div class="card-number">{{ item.amount }}</div>
|
||||||
|
<div class="card-label">{{ item.currency_code }}</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<div class="amount not-amount1">
|
||||||
|
<div class="title">未认领金额</div>
|
||||||
|
<div class="amount-grid not-accounted">
|
||||||
|
<el-row :gutter="15" justify="space-between">
|
||||||
|
<el-col
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
:md="2"
|
||||||
|
v-for="(item, key) in statisticscard.unclaimed_amount"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<div class="card-number">{{ item.amount }}</div>
|
||||||
|
<div class="card-label">{{ item.currency_code }}</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<div class="amount not-amount2">
|
||||||
|
<div class="title">未入账金额</div>
|
||||||
|
<div class="amount-grid not-account">
|
||||||
|
<el-row :gutter="15" justify="space-between">
|
||||||
|
<el-col
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
:md="2"
|
||||||
|
v-for="(item, key) in statisticscard.unreceived_amount"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<div class="card-number">{{ item.amount }}</div>
|
||||||
|
<div class="card-label">{{ item.currency_code }}</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="list-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">未入账</h3>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<ArtTable
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="id"
|
||||||
|
:loading="loadingwrz"
|
||||||
|
:data="tableDatawrz"
|
||||||
|
:currentPage="paginationwrz.currentPage"
|
||||||
|
:pageSize="paginationwrz.pageSize"
|
||||||
|
:total="paginationwrz.total"
|
||||||
|
@size-change="handleSizeChangewrz"
|
||||||
|
@current-change="handleCurrentChangewrz"
|
||||||
|
lass="customer-table"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<el-table-column prop="id" label="备货单号" min-width="140">
|
||||||
|
<template #default="scope">
|
||||||
|
<p class="colors" v-for="(item, index) in scope.row.stock_order_no" :key="index">
|
||||||
|
{{ item }}</p
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<ElTableColumn label="创建日期" prop="created_at" min-width="160" />
|
||||||
|
<ElTableColumn label="内勤" prop="doc_user_name" min-width="120" />
|
||||||
|
<ElTableColumn label="汇款人" prop="remitter" min-width="120" />
|
||||||
|
<ElTableColumn label="账户" prop="account_name" min-width="120" />
|
||||||
|
<ElTableColumn label="账币" prop="currency_name" min-width="120" />
|
||||||
|
<ElTableColumn label="金额" prop="amount" min-width="120" />
|
||||||
|
</template>
|
||||||
|
</ArtTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-row :gutter="15">
|
||||||
|
<el-col :span="12">
|
||||||
|
<div class="list-sectiont">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">超时未上传</h3>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<ArtTable
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="id"
|
||||||
|
:loading="loadingouttime"
|
||||||
|
:data="tableDataouttime"
|
||||||
|
:currentPage="paginationouttime.currentPage"
|
||||||
|
:pageSize="paginationouttime.pageSize"
|
||||||
|
:total="paginationouttime.total"
|
||||||
|
@size-change="handleSizeChangeouttime"
|
||||||
|
@current-change="handleCurrentChangeouttime"
|
||||||
|
lass="customer-table"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<el-table-column prop="id" label="备货单号" min-width="140">
|
||||||
|
<template #default="scope">
|
||||||
|
<p class="colors" v-for="(item, index) in scope.row.stock_order_no" :key="index">
|
||||||
|
{{ item }}</p
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<ElTableColumn label="创建日期" prop="created_at" min-width="160" />
|
||||||
|
<ElTableColumn label="内勤" prop="doc_user_name" min-width="120" />
|
||||||
|
<ElTableColumn label="汇款人" prop="remitter" min-width="120" />
|
||||||
|
<ElTableColumn label="账户" prop="account_name" min-width="120" />
|
||||||
|
<ElTableColumn label="账币" prop="currency_name" min-width="120" />
|
||||||
|
<ElTableColumn label="金额" prop="amount" min-width="120" />
|
||||||
|
</template>
|
||||||
|
</ArtTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<div class="list-sectiont">
|
||||||
|
<div class="section-header">
|
||||||
|
<div class="section-bar"></div>
|
||||||
|
<h3 class="section-title">已到账未认领</h3>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<ArtTable
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="id"
|
||||||
|
:loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
:currentPage="pagination.currentPage"
|
||||||
|
:pageSize="pagination.pageSize"
|
||||||
|
:total="pagination.total"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
lass="customer-table"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<ElTableColumn label="创建日期" prop="created_at" min-width="160" />
|
||||||
|
<!-- <ElTableColumn label="付款来源" prop="country_name" min-width="120" /> -->
|
||||||
|
<ElTableColumn label="汇款人" prop="remitter" min-width="120" />
|
||||||
|
<ElTableColumn label="账户" prop="account_name" min-width="120" />
|
||||||
|
<ElTableColumn label="账币" prop="currency_name" min-width="120" />
|
||||||
|
<ElTableColumn label="金额" prop="amount" min-width="120" />
|
||||||
|
</template>
|
||||||
|
</ArtTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { dashboardService } from '@/api/admin/dashboard'
|
||||||
|
import { financeService } from '@/api/admin/finance'
|
||||||
|
const loading = ref(false)
|
||||||
|
const loadingouttime = ref(false)
|
||||||
|
const loadingwrz = ref(false)
|
||||||
|
const pagination = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
const paginationouttime = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
const paginationwrz = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
})
|
||||||
|
const tableData = ref(<any>[])
|
||||||
|
const tableDataouttime = ref(<any>[])
|
||||||
|
const tableDatawrz = ref(<any>[])
|
||||||
|
// 处理表格分页变化
|
||||||
|
const handleSizeChange = (newPageSize: number) => {
|
||||||
|
pagination.pageSize = newPageSize
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChange = (newCurrentPage: number) => {
|
||||||
|
pagination.currentPage = newCurrentPage
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
const handleSizeChangeouttime = (newPageSize: number) => {
|
||||||
|
paginationouttime.pageSize = newPageSize
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChangeouttime = (newCurrentPage: number) => {
|
||||||
|
paginationouttime.currentPage = newCurrentPage
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
const handleSizeChangewrz = (newPageSize: number) => {
|
||||||
|
paginationwrz.pageSize = newPageSize
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCurrentChangewrz = (newCurrentPage: number) => {
|
||||||
|
paginationwrz.currentPage = newCurrentPage
|
||||||
|
// getWorkList()
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取卡片数据
|
||||||
|
const statisticscard = ref(<any>{})
|
||||||
|
const officeclerkcharts = async () => {
|
||||||
|
const response = await dashboardService.officeclerkchart()
|
||||||
|
statisticscard.value = response
|
||||||
|
}
|
||||||
|
//未认领列表
|
||||||
|
const getOfficeclerkunclaimed = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const { currentPage, pageSize } = pagination
|
||||||
|
const params = {
|
||||||
|
current: currentPage,
|
||||||
|
size: pageSize,
|
||||||
|
payment_apply: 'not'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { records, current, size, total } = await financeService.getReceiptList(params)
|
||||||
|
tableData.value = records || []
|
||||||
|
Object.assign(pagination, { currentPage: current, pageSize: size, total })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取列表失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//未提交资料
|
||||||
|
const getOfficepayment = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const { currentPage, pageSize } = paginationouttime
|
||||||
|
const params = {
|
||||||
|
current: currentPage,
|
||||||
|
size: pageSize,
|
||||||
|
scene: 'file_timeout'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { records, current, size, total } = await financeService.getPaymentList(params)
|
||||||
|
tableDataouttime.value = records || []
|
||||||
|
Object.assign(paginationouttime, { currentPage: current, pageSize: size, total })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取列表失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//未入账列表
|
||||||
|
const getOfficeclerkpending = async () => {
|
||||||
|
loadingouttime.value = true
|
||||||
|
try {
|
||||||
|
const { currentPage, pageSize } = paginationwrz
|
||||||
|
const params = {
|
||||||
|
current: currentPage,
|
||||||
|
size: pageSize,
|
||||||
|
scene: 'pending'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { records, current, size, total } = await financeService.getPaymentList(params)
|
||||||
|
tableDatawrz.value = records || []
|
||||||
|
Object.assign(paginationwrz, { currentPage: current, pageSize: size, total })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取列表失败:', error)
|
||||||
|
} finally {
|
||||||
|
loadingouttime.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
officeclerkcharts()
|
||||||
|
getOfficepayment()
|
||||||
|
getOfficeclerkunclaimed()
|
||||||
|
getOfficeclerkpending()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mt-20 {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.office-administrator {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background: #f0f9ff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-cards {
|
||||||
|
margin: 20px 0px 0px;
|
||||||
|
background: #fff;
|
||||||
|
// padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
// height: 230px;
|
||||||
|
.card-l {
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
padding: 45px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
img {
|
||||||
|
width: 73px;
|
||||||
|
}
|
||||||
|
.card-number {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
color: #009c54;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.card-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.card-r {
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
padding: 22px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: left;
|
||||||
|
.card-number {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: #009c54;
|
||||||
|
}
|
||||||
|
.card-label {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #666666;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
width: 62px;
|
||||||
|
}
|
||||||
|
.card-icon {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-sectiont {
|
||||||
|
// margin: 20px 0 0;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px 20px;
|
||||||
|
}
|
||||||
|
.ontheway-cny {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #3471ff;
|
||||||
|
text-align: center;
|
||||||
|
margin: 23px 0px 15px;
|
||||||
|
}
|
||||||
|
.ontheway-num {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
text-align: center;
|
||||||
|
margin: 23px 0px 15px;
|
||||||
|
}
|
||||||
|
.main-content {
|
||||||
|
margin: -20px;
|
||||||
|
}
|
||||||
|
.ontheway {
|
||||||
|
background: white;
|
||||||
|
padding: 5px 20px;
|
||||||
|
text-align: center;
|
||||||
|
.card-number {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
color: #3471ff;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.card-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-section {
|
||||||
|
margin: 20px 0;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.data-summary {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px 20px;
|
||||||
|
min-height: 208px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
// box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
// height: 230px;
|
||||||
|
|
||||||
|
.summary-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.summary-item {
|
||||||
|
text-align: left;
|
||||||
|
padding: 12px 15px;
|
||||||
|
// background: #f8f9fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
position: relative;
|
||||||
|
&.claimed {
|
||||||
|
background: #f3fcff;
|
||||||
|
}
|
||||||
|
&.unclaimed {
|
||||||
|
background: #fcfaff;
|
||||||
|
}
|
||||||
|
&.overdue {
|
||||||
|
background: #f1fcf6;
|
||||||
|
}
|
||||||
|
&.not-accounted {
|
||||||
|
background: #fff9f6;
|
||||||
|
}
|
||||||
|
.summary-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-number {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&.blue {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.purple {
|
||||||
|
color: #9c27b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-tag {
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 12px;
|
||||||
|
&.blue {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.purple {
|
||||||
|
color: #9c27b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-amount {
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 14px;
|
||||||
|
background: white;
|
||||||
|
.amount {
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: linear-gradient(102.34deg, #d7ffcd 1.87%, #f1ffee 53.37%, #daf6d3 101.98%);
|
||||||
|
&.not-amount1 {
|
||||||
|
background: linear-gradient(102.34deg, #fffad3 1.87%, #fffadb 53.37%, #fdf5c1 101.98%);
|
||||||
|
}
|
||||||
|
&.not-amount2 {
|
||||||
|
background: linear-gradient(102.34deg, #ffd6d6 1.87%, #fff0f0 53.37%, #fdc5c5 101.98%);
|
||||||
|
}
|
||||||
|
.amount-grid {
|
||||||
|
margin-top: 11px;
|
||||||
|
text-align: center;
|
||||||
|
.card-number {
|
||||||
|
color: #25ba00;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
&.not-accounted .card-number {
|
||||||
|
color: #ff8800;
|
||||||
|
}
|
||||||
|
&.not-account .card-number {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
.card-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666666;
|
||||||
|
margin: 5px 0px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unapproved-list {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.item-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-date {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-action {
|
||||||
|
.el-link {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.section-bar {
|
||||||
|
width: 4px;
|
||||||
|
height: 20px;
|
||||||
|
background: #409eff;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||