guzhi/web1/src/views/user-center/components/InvoiceManagement.vue
2025-11-24 18:01:39 +08:00

438 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="content-section">
<div class="section-header">
<div class="header-left">
<div class="title-bar"></div>
<div class="title-text">抬头管理</div>
</div>
<div class="add-btn" @click="handleAddInvoice">
<span class="plus-icon">+</span>
<span>添加抬头</span>
</div>
</div>
<div class="table-container">
<n-data-table
:columns="columns"
:data="invoiceList"
:row-key="rowKey"
:pagination="false"
striped
size="medium"
/>
</div>
<!-- 添加/编辑抬头弹窗 -->
<n-modal
v-model:show="showModal"
:mask-closable="false"
preset="card"
:title="currentInvoice ? '编辑抬头' : '添加抬头'"
class="invoice-modal"
:style="{ width: '600px' }"
:bordered="false"
:segmented="{ content: true }"
>
<n-form
ref="formRef"
:model="formData"
:rules="formRules"
label-placement="top"
require-mark-placement="left"
:show-feedback="true"
>
<n-grid :cols="2" :x-gap="16">
<!-- 公司名称 -->
<n-form-item-gi :span="1" path="company_name" label="公司名称">
<n-input v-model:value="formData.company_name" placeholder="请输入公司名称" clearable />
</n-form-item-gi>
<!-- 公司税号 -->
<n-form-item-gi :span="1" path="tax_number" label="公司税号">
<n-input v-model:value="formData.tax_number" placeholder="请输入公司税号" clearable />
</n-form-item-gi>
<!-- 注册地址 -->
<n-form-item-gi :span="2" path="register_address" label="注册地址">
<n-input
v-model:value="formData.register_address"
placeholder="请输入注册地址"
clearable
/>
</n-form-item-gi>
<!-- 注册电话 -->
<n-form-item-gi :span="2" path="register_phone" label="注册电话">
<n-input
v-model:value="formData.register_phone"
placeholder="请输入注册电话"
clearable
/>
</n-form-item-gi>
<!-- 开户银行 -->
<n-form-item-gi :span="1" path="bank_name" label="开户银行">
<n-input v-model:value="formData.bank_name" placeholder="请输入开户银行" clearable />
</n-form-item-gi>
<!-- 银行账号 -->
<n-form-item-gi :span="1" path="bank_account" label="银行账号">
<n-input v-model:value="formData.bank_account" placeholder="请输入银行账号" clearable />
</n-form-item-gi>
<!-- 电子邮箱 -->
<n-form-item-gi :span="2" path="email" label="电子邮箱">
<n-input v-model:value="formData.email" placeholder="请输入邮箱" clearable />
<template #feedback>
<span class="email-hint">此邮箱用于接收发票请填写正确</span>
</template>
</n-form-item-gi>
</n-grid>
</n-form>
<template #footer>
<div class="modal-footer">
<n-button class="cancel-btn" @click="handleCancel">取消</n-button>
<n-button class="save-btn" @click="handleSave">保存</n-button>
</div>
</template>
</n-modal>
</div>
</template>
<script setup>
import { h, ref, watch } from 'vue'
import {
NButton,
NDataTable,
NSpace,
NModal,
NForm,
NFormItemGi,
NGrid,
NInput,
NPopconfirm,
} from 'naive-ui'
import { renderIcon } from '@/utils'
const props = defineProps({
invoiceList: {
type: Array,
default: () => [],
},
})
const emit = defineEmits(['add-invoice', 'delete-invoice', 'update-invoice'])
const showModal = ref(false)
const currentInvoice = ref(null)
const formRef = ref(null)
// 表单数据
const formData = ref({
company_name: '',
tax_number: '',
register_address: '',
register_phone: '',
bank_name: '',
bank_account: '',
email: '',
})
// 表单验证规则
const formRules = {
company_name: [{ required: true, message: '请输入公司名称', trigger: 'blur' }],
tax_number: [{ required: true, message: '请输入公司税号', trigger: 'blur' }],
email: [
{ required: true, message: '请输入电子邮箱', trigger: 'blur' },
{
pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
message: '请输入正确的邮箱格式',
trigger: 'blur',
},
],
}
// 监听弹窗显示状态,重置表单
watch(showModal, (newVal) => {
if (newVal) {
if (currentInvoice.value) {
// 编辑模式,填充数据
formData.value = { ...currentInvoice.value }
} else {
// 新增模式,清空数据
formData.value = {
company_name: '',
tax_number: '',
register_address: '',
register_phone: '',
bank_name: '',
bank_account: '',
email: '',
}
}
}
})
// 添加开票
function handleAddInvoice() {
currentInvoice.value = null
showModal.value = true
}
// 编辑开票
function handleEditInvoice(invoice) {
currentInvoice.value = { ...invoice }
showModal.value = true
}
// 删除开票
function handleDeleteInvoice(invoice) {
// TODO: Add confirmation dialog
emit('delete-invoice', invoice)
}
// 取消
function handleCancel() {
showModal.value = false
formRef.value?.restoreValidation()
}
// 保存
function handleSave() {
formRef.value?.validate((errors) => {
if (!errors) {
handleModalSubmit(formData.value)
}
})
}
// 处理弹窗提交
function handleModalSubmit(data) {
if (currentInvoice.value) {
emit('update-invoice', { ...currentInvoice.value, ...data })
} else {
emit('add-invoice', data)
}
showModal.value = false
}
const columns = [
{
title: '公司名称',
key: 'name',
minWidth: 220,
ellipsis: true,
align: 'center',
titleAlign: 'center',
render: (row) => row.name || row.company_name || '-',
},
{
title: '税号',
key: 'taxId',
minWidth: 200,
align: 'center',
titleAlign: 'center',
render: (row) => row.taxId || row.tax_number || '123456789012345678',
},
{
title: '操作',
key: 'actions',
width: 200,
align: 'center',
titleAlign: 'center',
render: (row) =>
h(
NSpace,
{ size: 12, justify: 'center' },
{
default: () => [
h(
NButton,
{
text: true,
type: 'primary',
size: 'small',
onClick: () => handleEditInvoice(row),
},
{
default: () => '编辑',
icon: renderIcon('material-symbols:edit', { size: 16 }),
}
),
h(
NPopconfirm,
{
onPositiveClick: () => handleDeleteInvoice(row),
negativeText: '取消',
positiveText: '确定',
},
{
trigger: () =>
h(
NButton,
{
text: true,
type: 'error',
size: 'small',
},
{
default: () => '删除',
icon: renderIcon('material-symbols:delete-outline', { size: 16 }),
}
),
default: () => '确定删除该发票抬头吗?',
}
),
],
}
),
},
]
const rowKey = (row) => row.id ?? row.name ?? row.company_name ?? row.taxId ?? row.tax_number
</script>
<style scoped>
.content-section {
background: white;
border-radius: 8px;
padding: 24px;
min-height: calc(100vh - 40px);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.header-left {
display: flex;
align-items: center;
gap: 8px;
}
.title-bar {
width: 4px;
height: 16px;
background: #a30113;
border-radius: 2px;
}
.title-text {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.add-btn {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 16px;
background: #a30113;
color: white;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: background 0.3s;
}
.add-btn:hover {
background: #880c22;
}
.plus-icon {
font-size: 18px;
font-weight: bold;
line-height: 1;
}
.table-container {
width: 100%;
}
/* 弹窗样式 */
:deep(.invoice-modal) {
border-radius: 8px;
}
:deep(.invoice-modal .n-card-header) {
padding: 20px 24px;
border-bottom: 1px solid #f0f0f0;
font-size: 16px;
font-weight: 600;
color: #303133;
}
:deep(.invoice-modal .n-card__content) {
padding: 24px;
}
:deep(.n-form-item-label) {
font-size: 14px;
color: #303133;
font-weight: 500;
margin-bottom: 8px;
}
:deep(.n-form-item-label__asterisk) {
color: #a30113;
}
:deep(.n-input) {
border-radius: 4px;
}
:deep(.n-input__input-el::placeholder) {
color: #c0c4cc;
}
.email-hint {
font-size: 12px;
color: #a30113;
margin-top: 4px;
display: inline-block;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
padding-top: 16px;
}
.cancel-btn {
padding: 8px 24px;
border: 1px solid #dcdfe6;
background: white;
color: #606266;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s;
}
.cancel-btn:hover {
border-color: #c0c4cc;
background: #f5f7fa;
}
.save-btn {
padding: 8px 24px;
background: #a30113;
color: white;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: background 0.3s;
}
.save-btn:hover {
background: #880c22;
}
</style>