438 lines
9.7 KiB
Vue
438 lines
9.7 KiB
Vue
<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>
|