feat: 新增交易管理模块和开票记录功能
- 新增交易管理一级菜单目录 - 新增开票记录二级菜单 - 实现开票记录列表页面,包含搜索、状态管理等功能 - 添加开票记录相关API接口定义
This commit is contained in:
parent
9337f06f6e
commit
f468a87fdd
@ -45,4 +45,12 @@ export default {
|
|||||||
createAppUser: (data = {}) => request.post('/app-user/register', data),
|
createAppUser: (data = {}) => request.post('/app-user/register', data),
|
||||||
updateAppUser: (data = {}) => request.post('/app-user/update', data),
|
updateAppUser: (data = {}) => request.post('/app-user/update', data),
|
||||||
deleteAppUser: (params = {}) => request.delete('/app-user/delete', { params }),
|
deleteAppUser: (params = {}) => request.delete('/app-user/delete', { params }),
|
||||||
|
// invoice (开票记录)
|
||||||
|
getInvoiceList: (params = {}) => request.get('/invoice/list', { params }),
|
||||||
|
getInvoiceById: (params = {}) => request.get('/invoice/detail', { params }),
|
||||||
|
createInvoice: (data = {}) => request.post('/invoice/create', data),
|
||||||
|
updateInvoice: (data = {}) => request.post('/invoice/update', data),
|
||||||
|
deleteInvoice: (params = {}) => request.delete('/invoice/delete', { params }),
|
||||||
|
updateInvoiceStatus: (data = {}) => request.post('/invoice/update-status', data),
|
||||||
|
remindInvoice: (data = {}) => request.post('/invoice/remind', data),
|
||||||
}
|
}
|
||||||
|
|||||||
455
web/src/views/transaction/invoice/index.vue
Normal file
455
web/src/views/transaction/invoice/index.vue
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
<script setup>
|
||||||
|
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
||||||
|
import {
|
||||||
|
NButton,
|
||||||
|
NForm,
|
||||||
|
NFormItem,
|
||||||
|
NInput,
|
||||||
|
NTag,
|
||||||
|
NPopconfirm,
|
||||||
|
NSelect,
|
||||||
|
NDatePicker,
|
||||||
|
} from 'naive-ui'
|
||||||
|
|
||||||
|
import CommonPage from '@/components/page/CommonPage.vue'
|
||||||
|
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
||||||
|
import CrudModal from '@/components/table/CrudModal.vue'
|
||||||
|
import CrudTable from '@/components/table/CrudTable.vue'
|
||||||
|
|
||||||
|
import { formatDate, renderIcon } from '@/utils'
|
||||||
|
import { useCRUD } from '@/composables'
|
||||||
|
import api from '@/api'
|
||||||
|
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: '开票记录' })
|
||||||
|
|
||||||
|
const $table = ref(null)
|
||||||
|
const queryItems = ref({})
|
||||||
|
const vPermission = resolveDirective('permission')
|
||||||
|
|
||||||
|
// 状态选项
|
||||||
|
const statusOptions = [
|
||||||
|
{ label: '全部', value: '' },
|
||||||
|
{ label: '未开票', value: 'pending' },
|
||||||
|
{ label: '已开票', value: 'invoiced' },
|
||||||
|
{ label: '已拒绝', value: 'rejected' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 开票类型选项
|
||||||
|
const invoiceTypeOptions = [
|
||||||
|
{ label: '增值税普通发票', value: 'normal' },
|
||||||
|
{ label: '增值税专用发票', value: 'special' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 供票类型选项
|
||||||
|
const ticketTypeOptions = [
|
||||||
|
{ label: '纸质发票', value: 'paper' },
|
||||||
|
{ label: '电子发票', value: 'electronic' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalVisible,
|
||||||
|
modalTitle,
|
||||||
|
modalAction,
|
||||||
|
modalLoading,
|
||||||
|
handleSave,
|
||||||
|
modalForm,
|
||||||
|
modalFormRef,
|
||||||
|
handleEdit,
|
||||||
|
handleDelete,
|
||||||
|
handleAdd,
|
||||||
|
} = useCRUD({
|
||||||
|
name: '开票记录',
|
||||||
|
initForm: {},
|
||||||
|
doCreate: api.createInvoice,
|
||||||
|
doUpdate: api.updateInvoice,
|
||||||
|
doDelete: api.deleteInvoice,
|
||||||
|
refresh: () => $table.value?.handleSearch(),
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
$table.value?.handleSearch()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 状态标签渲染
|
||||||
|
const renderStatus = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
pending: { type: 'warning', text: '未开票' },
|
||||||
|
invoiced: { type: 'success', text: '已开票' },
|
||||||
|
rejected: { type: 'error', text: '已拒绝' },
|
||||||
|
}
|
||||||
|
const config = statusMap[status] || { type: 'default', text: '未知' }
|
||||||
|
return h(NTag, { type: config.type }, { default: () => config.text })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开票类型渲染
|
||||||
|
const renderInvoiceType = (type) => {
|
||||||
|
const typeMap = {
|
||||||
|
normal: '增值税普通发票',
|
||||||
|
special: '增值税专用发票',
|
||||||
|
}
|
||||||
|
return typeMap[type] || type
|
||||||
|
}
|
||||||
|
|
||||||
|
// 供票类型渲染
|
||||||
|
const renderTicketType = (type) => {
|
||||||
|
const typeMap = {
|
||||||
|
paper: '纸质发票',
|
||||||
|
electronic: '电子发票',
|
||||||
|
}
|
||||||
|
return typeMap[type] || type
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
key: 'id',
|
||||||
|
width: 60,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '提交时间',
|
||||||
|
key: 'created_at',
|
||||||
|
width: 100,
|
||||||
|
align: 'center',
|
||||||
|
render(row) {
|
||||||
|
return formatDate(row.created_at)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '供票类型',
|
||||||
|
key: 'ticket_type',
|
||||||
|
width: 80,
|
||||||
|
align: 'center',
|
||||||
|
render(row) {
|
||||||
|
return renderTicketType(row.ticket_type)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '手机号',
|
||||||
|
key: 'phone',
|
||||||
|
width: 100,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '邮箱号',
|
||||||
|
key: 'email',
|
||||||
|
width: 120,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '公司名称',
|
||||||
|
key: 'company_name',
|
||||||
|
width: 150,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '公司税号',
|
||||||
|
key: 'tax_number',
|
||||||
|
width: 150,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '注册地址',
|
||||||
|
key: 'register_address',
|
||||||
|
width: 150,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '注册电话',
|
||||||
|
key: 'register_phone',
|
||||||
|
width: 100,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开户银行',
|
||||||
|
key: 'bank_name',
|
||||||
|
width: 120,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '银行账号',
|
||||||
|
key: 'bank_account',
|
||||||
|
width: 150,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开票类型',
|
||||||
|
key: 'invoice_type',
|
||||||
|
width: 120,
|
||||||
|
align: 'center',
|
||||||
|
render(row) {
|
||||||
|
return renderInvoiceType(row.invoice_type)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'status',
|
||||||
|
width: 80,
|
||||||
|
align: 'center',
|
||||||
|
render(row) {
|
||||||
|
return renderStatus(row.status)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'actions',
|
||||||
|
width: 200,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
render(row) {
|
||||||
|
return [
|
||||||
|
// 开票提醒按钮
|
||||||
|
row.status === 'pending' &&
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: 'info',
|
||||||
|
style: 'margin-right: 8px;',
|
||||||
|
onClick: () => handleRemind(row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => '开票提醒',
|
||||||
|
icon: renderIcon('mdi:bell-outline', { size: 16 }),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// 已开票按钮
|
||||||
|
row.status === 'pending' &&
|
||||||
|
h(
|
||||||
|
NPopconfirm,
|
||||||
|
{
|
||||||
|
onPositiveClick: () => handleUpdateStatus(row, 'invoiced'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trigger: () =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: 'success',
|
||||||
|
style: 'margin-right: 8px;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => '已开票',
|
||||||
|
icon: renderIcon('mdi:check-circle-outline', { size: 16 }),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => h('div', {}, '确认已开票?'),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// 已拒绝按钮
|
||||||
|
row.status === 'pending' &&
|
||||||
|
h(
|
||||||
|
NPopconfirm,
|
||||||
|
{
|
||||||
|
onPositiveClick: () => handleUpdateStatus(row, 'rejected'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trigger: () =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: 'error',
|
||||||
|
style: 'margin-right: 8px;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => '已拒绝',
|
||||||
|
icon: renderIcon('mdi:close-circle-outline', { size: 16 }),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => h('div', {}, '确认拒绝开票?'),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 开票提醒
|
||||||
|
async function handleRemind(row) {
|
||||||
|
try {
|
||||||
|
await api.remindInvoice({ id: row.id })
|
||||||
|
$message.success('提醒发送成功')
|
||||||
|
} catch (error) {
|
||||||
|
$message.error('提醒发送失败: ' + error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
async function handleUpdateStatus(row, status) {
|
||||||
|
try {
|
||||||
|
await api.updateInvoiceStatus({ id: row.id, status })
|
||||||
|
$message.success('状态更新成功')
|
||||||
|
$table.value?.handleSearch()
|
||||||
|
} catch (error) {
|
||||||
|
$message.error('状态更新失败: ' + error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateForm = {
|
||||||
|
company_name: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入公司名称',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tax_number: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入公司税号',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
phone: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入手机号',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入邮箱',
|
||||||
|
trigger: ['input', 'blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CommonPage show-footer title="开票记录">
|
||||||
|
<!-- 表格 -->
|
||||||
|
<CrudTable
|
||||||
|
ref="$table"
|
||||||
|
v-model:query-items="queryItems"
|
||||||
|
:columns="columns"
|
||||||
|
:get-data="api.getInvoiceList"
|
||||||
|
>
|
||||||
|
<template #queryBar>
|
||||||
|
<QueryBarItem label="提交时间" :label-width="80">
|
||||||
|
<NDatePicker
|
||||||
|
v-model:value="queryItems.created_at"
|
||||||
|
type="daterange"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择提交时间"
|
||||||
|
style="width: 240px"
|
||||||
|
@update:value="$table?.handleSearch()"
|
||||||
|
/>
|
||||||
|
</QueryBarItem>
|
||||||
|
<QueryBarItem label="手机号" :label-width="60">
|
||||||
|
<NInput
|
||||||
|
v-model:value="queryItems.phone"
|
||||||
|
clearable
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
@keypress.enter="$table?.handleSearch()"
|
||||||
|
/>
|
||||||
|
</QueryBarItem>
|
||||||
|
<QueryBarItem label="状态" :label-width="40">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="queryItems.status"
|
||||||
|
:options="statusOptions"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
clearable
|
||||||
|
style="width: 150px"
|
||||||
|
@update:value="$table?.handleSearch()"
|
||||||
|
/>
|
||||||
|
</QueryBarItem>
|
||||||
|
<QueryBarItem label="公司名称" :label-width="80">
|
||||||
|
<NInput
|
||||||
|
v-model:value="queryItems.company_name"
|
||||||
|
clearable
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入公司名称"
|
||||||
|
@keypress.enter="$table?.handleSearch()"
|
||||||
|
/>
|
||||||
|
</QueryBarItem>
|
||||||
|
<QueryBarItem label="公司税号" :label-width="80">
|
||||||
|
<NInput
|
||||||
|
v-model:value="queryItems.tax_number"
|
||||||
|
clearable
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入公司税号"
|
||||||
|
@keypress.enter="$table?.handleSearch()"
|
||||||
|
/>
|
||||||
|
</QueryBarItem>
|
||||||
|
</template>
|
||||||
|
</CrudTable>
|
||||||
|
|
||||||
|
<!-- 新增/编辑 弹窗 -->
|
||||||
|
<CrudModal
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="modalTitle"
|
||||||
|
:loading="modalLoading"
|
||||||
|
@save="handleSave"
|
||||||
|
>
|
||||||
|
<NForm
|
||||||
|
ref="modalFormRef"
|
||||||
|
label-placement="left"
|
||||||
|
label-align="left"
|
||||||
|
:label-width="100"
|
||||||
|
:model="modalForm"
|
||||||
|
:rules="validateForm"
|
||||||
|
>
|
||||||
|
<NFormItem label="公司名称" path="company_name">
|
||||||
|
<NInput v-model:value="modalForm.company_name" clearable placeholder="请输入公司名称" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="公司税号" path="tax_number">
|
||||||
|
<NInput v-model:value="modalForm.tax_number" clearable placeholder="请输入公司税号" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="手机号" path="phone">
|
||||||
|
<NInput v-model:value="modalForm.phone" clearable placeholder="请输入手机号" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="邮箱" path="email">
|
||||||
|
<NInput v-model:value="modalForm.email" clearable placeholder="请输入邮箱" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="注册地址" path="register_address">
|
||||||
|
<NInput
|
||||||
|
v-model:value="modalForm.register_address"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入注册地址"
|
||||||
|
/>
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="注册电话" path="register_phone">
|
||||||
|
<NInput
|
||||||
|
v-model:value="modalForm.register_phone"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入注册电话"
|
||||||
|
/>
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="开户银行" path="bank_name">
|
||||||
|
<NInput v-model:value="modalForm.bank_name" clearable placeholder="请输入开户银行" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="银行账号" path="bank_account">
|
||||||
|
<NInput v-model:value="modalForm.bank_account" clearable placeholder="请输入银行账号" />
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="供票类型" path="ticket_type">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="modalForm.ticket_type"
|
||||||
|
:options="ticketTypeOptions"
|
||||||
|
placeholder="请选择供票类型"
|
||||||
|
/>
|
||||||
|
</NFormItem>
|
||||||
|
<NFormItem label="开票类型" path="invoice_type">
|
||||||
|
<NSelect
|
||||||
|
v-model:value="modalForm.invoice_type"
|
||||||
|
:options="invoiceTypeOptions"
|
||||||
|
placeholder="请选择开票类型"
|
||||||
|
/>
|
||||||
|
</NFormItem>
|
||||||
|
</NForm>
|
||||||
|
</CrudModal>
|
||||||
|
</CommonPage>
|
||||||
|
</template>
|
||||||
Loading…
x
Reference in New Issue
Block a user