jiaxing.zhou

Merge branch 'master' into dev

# Conflicts:
#	frontend/src/api/delivery.ts
#	frontend/src/api/invoice.ts
#	frontend/src/views/delivery/index.vue
#	frontend/src/views/invoice/index.vue
......@@ -40,7 +40,7 @@ public class DeliveryAddReq {
@NotNull(message = "出库日期不能为空")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "出库日期", required = true)
private LocalDateTime deliveryDate;
private String deliveryDate;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
......
......@@ -44,15 +44,9 @@ public class DeliveryMain {
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "出库总金额")
private BigDecimal totalAmount;
@Schema(description = "仓库编码")
private String warehouseCode;
@Schema(description = "仓库名称")
private String warehouseName;
@Schema(description = "数据来源")
private String dataSource;
......
......@@ -111,6 +111,9 @@ public class DeliveryMainServiceImpl extends ServiceImpl<DeliveryMainMapper, Del
BeanUtils.copyProperties(addReq, deliveryMain);
deliveryMain.setCreateTime(LocalDateTime.now());
deliveryMain.setUpdateTime(LocalDateTime.now());
if(StringUtils.hasText(addReq.getDeliveryDate())) {
deliveryMain.setDeliveryDate(parseDateTime(addReq.getDeliveryDate()));
}
deliveryMain.setDelFlag("0");
int insert = baseMapper.insert(deliveryMain);
if (insert > 0) {
......
......@@ -35,12 +35,15 @@ VALUES
INSERT INTO t_sys_menu (menu_id, parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag)
VALUES
(301, 3, '出库查询', '2', '', '', 1, '1', 'delivery:list', 'admin', NOW(), 'admin', NOW(), '0'),
(302, 3, '出库详情', '2', '', '', 2, '1', 'delivery:query', 'admin', NOW(), 'admin', NOW(), '0'),
(303, 3, '出库导出', '2', '', '', 3, '1', 'delivery:export', 'admin', NOW(), 'admin', NOW(), '0'),
(304, 3, '出库统计', '2', '', '', 4, '1', 'delivery:statistics', 'admin', NOW(), 'admin', NOW(), '0'),
(305, 3, '出库打印', '2', '', '', 5, '1', 'delivery:print', 'admin', NOW(), 'admin', NOW(), '0'),
(306, 3, '出库确认', '2', '', '', 6, '1', 'delivery:confirm', 'admin', NOW(), 'admin', NOW(), '0'),
(307, 3, '出库撤销', '2', '', '', 7, '1', 'delivery:cancel', 'admin', NOW(), 'admin', NOW(), '0');
(302, 3, '出库新增', '2', '', '', 2, '1', 'delivery:add', 'admin', NOW(), 'admin', NOW(), '0'),
(303, 3, '出库修改', '2', '', '', 3, '1', 'delivery:edit', 'admin', NOW(), 'admin', NOW(), '0'),
(304, 3, '出库删除', '2', '', '', 4, '1', 'delivery:delete', 'admin', NOW(), 'admin', NOW(), '0'),
(305, 3, '出库详情', '2', '', '', 5, '1', 'delivery:detail', 'admin', NOW(), 'admin', NOW(), '0'),
(306, 3, '出库导出', '2', '', '', 6, '1', 'delivery:export', 'admin', NOW(), 'admin', NOW(), '0'),
(307, 3, '出库统计', '2', '', '', 7, '1', 'delivery:statistics', 'admin', NOW(), 'admin', NOW(), '0'),
(308, 3, '出库打印', '2', '', '', 8, '1', 'delivery:print', 'admin', NOW(), 'admin', NOW(), '0'),
(309, 3, '出库确认', '2', '', '', 9, '1', 'delivery:confirm', 'admin', NOW(), 'admin', NOW(), '0'),
(310, 3, '出库撤销', '2', '', '', 10, '1', 'delivery:cancel', 'admin', NOW(), 'admin', NOW(), '0');
-- =========================================
-- 3. 插入发票管理相关按钮权限
......@@ -78,7 +81,7 @@ AND del_flag = '0';
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @admin_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (301, 302, 303, 304, 305, 306, 307)
WHERE menu_id IN (301, 302, 303, 304, 305, 306, 307, 308, 309, 310)
AND del_flag = '0';
-- 为ADMIN角色分配发票管理按钮权限
......@@ -102,11 +105,11 @@ FROM t_sys_menu
WHERE menu_id IN (201, 202, 203, 205, 206, 207, 208, 209, 210)
AND del_flag = '0';
-- 为OPERATOR角色分配出库查询按钮权限
-- 为OPERATOR角色分配出库查询按钮权限(除删除外)
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @operator_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (301, 302, 303, 304, 305, 306, 307)
WHERE menu_id IN (301, 302, 303, 305, 306, 307, 308, 309, 310)
AND del_flag = '0';
-- 为OPERATOR角色分配发票管理按钮权限(除删除外)
......@@ -130,11 +133,11 @@ FROM t_sys_menu
WHERE menu_id IN (201, 205, 206)
AND del_flag = '0';
-- 为DEALER_MANAGER角色分配出库查询按钮权限
-- 为DEALER_MANAGER角色分配出库查询按钮权限(只读权限)
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @dealer_manager_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (301, 302, 303, 304, 305)
WHERE menu_id IN (301, 305, 306, 307, 308)
AND del_flag = '0';
-- 为DEALER_MANAGER角色分配发票管理按钮权限(只读权限)
......
-- Apple经销商ERP系统 - 修复出库新增权限
-- 创建时间: 2025-01-27
-- 版本: v1.0
-- 设置数据库和字符集
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 使用数据库
USE apple_erp;
-- =========================================
-- 1. 添加缺失的出库新增权限
-- =========================================
-- 检查是否已存在出库新增权限
SET @delivery_add_exists = (SELECT COUNT(*) FROM t_sys_menu WHERE perms = 'delivery:add' AND del_flag = '0');
-- 如果不存在,则添加出库新增权限
INSERT INTO t_sys_menu (menu_id, parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag)
SELECT 302, 3, '出库新增', '2', '', '', 2, '1', 'delivery:add', 'admin', NOW(), 'admin', NOW(), '0'
WHERE @delivery_add_exists = 0;
-- =========================================
-- 2. 为所有角色分配出库新增权限
-- =========================================
-- 获取ADMIN角色ID
SET @admin_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'ADMIN' AND del_flag = '0' LIMIT 1);
-- 为ADMIN角色分配出库新增权限
INSERT IGNORE INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @admin_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE perms = 'delivery:add' AND del_flag = '0';
-- 获取OPERATOR角色ID
SET @operator_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'OPERATOR' AND del_flag = '0' LIMIT 1);
-- 为OPERATOR角色分配出库新增权限
INSERT IGNORE INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @operator_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE perms = 'delivery:add' AND del_flag = '0';
-- 获取DEALER_MANAGER角色ID
SET @dealer_manager_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'DEALER_MANAGER' AND del_flag = '0' LIMIT 1);
-- 为DEALER_MANAGER角色分配出库新增权限
INSERT IGNORE INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @dealer_manager_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE perms = 'delivery:add' AND del_flag = '0';
-- 重置外键检查
SET FOREIGN_KEY_CHECKS = 1;
-- 输出完成信息
SELECT '出库新增权限修复完成!' AS message;
......@@ -3,13 +3,15 @@ import { request } from '../utils/request'
// 出库相关类型定义
export interface DeliveryInfo {
deliveryId: number
orderId: number
orderNo: string
deliveryNo: string
dealerCode: string
dealerName: string
orderNo: string
deliveryDate: string
totalAmount: number
deliveryStatus: number
warehouseCode: string
dataSource: string
uploadTime?: string
createBy?: string
createTime?: string
updateBy?: string
......@@ -22,42 +24,73 @@ export interface DeliveryItemInfo {
deliveryId?: number
productCode: string
productName: string
productQty: number
unitPrice: number
totalPrice: number
createBy?: string
createTime?: string
deliveryQty: number
deliveryPrice: number
deliveryAmount: number
}
export interface DeliveryQueryReq {
pageNum?: number
pageSize?: number
orderNo?: string
deliveryNo?: string
dealerCode?: string
dealerName?: string
deliveryStatus?: number
warehouseCode?: string
dataSource?: string
deliveryStartDate?: string
deliveryEndDate?: string
deliveryStatus?: number
minAmount?: number
maxAmount?: number
pageNum?: number
pageSize?: number
}
export interface DeliveryAddReq {
orderId: number
deliveryNo: string
dealerCode: string
dealerName: string
orderNo: string
deliveryDate: string
deliveryItems: Omit<DeliveryItemInfo, 'itemId' | 'deliveryId' | 'createBy' | 'createTime'>[]
deliveryStatus?: number
warehouseCode: string
dataSource?: string
deliveryItems: DeliveryItemAddReq[]
}
export interface DeliveryItemAddReq {
productCode: string
productName: string
deliveryQty: number
deliveryPrice: number
deliveryAmount: number
}
export interface DeliveryUpdateReq {
deliveryId: number
deliveryDate?: string
deliveryNo: string
dealerCode: string
dealerName: string
orderNo: string
deliveryDate: string
deliveryStatus?: number
deliveryItems?: Omit<DeliveryItemInfo, 'itemId' | 'deliveryId' | 'createBy' | 'createTime'>[]
warehouseCode: string
dataSource?: string
deliveryItems: DeliveryItemUpdateReq[]
}
export interface DeliveryItemUpdateReq {
itemId?: number
productCode: string
productName: string
deliveryQty: number
deliveryPrice: number
deliveryAmount: number
}
// 出库API接口
export const deliveryApi = {
// 分页查询出库列表
getDeliveryList: (params: DeliveryQueryReq) => {
return request.get('/api/delivery/list', { params })
return request.get('/api/delivery/list', params)
},
// 获取出库详情
......@@ -67,10 +100,10 @@ export const deliveryApi = {
// 新增出库
addDelivery: (data: DeliveryAddReq) => {
return request.post('/api/delivery/add', data)
return request.post('/api/delivery', data)
},
// 更新出库
// 修改出库
updateDelivery: (data: DeliveryUpdateReq) => {
return request.post('/api/delivery/update', data)
},
......@@ -81,14 +114,14 @@ export const deliveryApi = {
},
// 批量删除出库
batchDeleteDelivery: (deliveryIds: number[]) => {
batchDeleteDeliveries: (deliveryIds: number[]) => {
return request.post('/api/delivery/batchDelete', deliveryIds)
},
// 导出出库数据
exportDelivery: (params: DeliveryQueryReq) => {
return request.get('/api/delivery/export', { params, responseType: 'blob' })
// 修改出库状态
updateDeliveryStatus: (deliveryId: number, deliveryStatus: number) => {
return request.post(`/api/delivery/${deliveryId}/deliveryStatus`, null, {
params: { deliveryStatus }
})
}
}
export default deliveryApi
......
import { request } from '../utils/request'
// 发票相关类型定义
export interface InvoiceInfo {
invoiceId: number
orderId: number
invoiceNo: string
orderNo: string
deliveryNo: string
dealerCode: string
dealerName: string
invoiceDate: string
totalAmount: number
invoiceDate: string
invoiceStatus: number
invoiceNo?: string
taxRate: number
dataSource: string
uploadTime?: string
createBy?: string
createTime?: string
updateBy?: string
......@@ -19,79 +21,108 @@ export interface InvoiceInfo {
}
export interface InvoiceItemInfo {
itemId?: number
invoiceId?: number
itemId: number
invoiceId: number
invoiceNo: string
productCode: string
productName: string
productQty: number
unitPrice: number
totalPrice: number
itemAmount: number
taxRate: number
taxAmount: number
createBy?: string
createTime?: string
updateBy?: string
updateTime?: string
}
export interface InvoiceQueryReq {
pageNum?: number
pageSize?: number
invoiceNo?: string
orderNo?: string
deliveryNo?: string
dealerCode?: string
dealerName?: string
invoiceStatus?: number
dataSource?: string
invoiceStartDate?: string
invoiceEndDate?: string
invoiceStatus?: number
pageNum: number
pageSize: number
}
export interface InvoiceAddReq {
orderId: number
invoiceNo: string
orderNo: string
deliveryNo: string
dealerCode: string
dealerName: string
totalAmount: number
invoiceDate: string
invoiceNo?: string
invoiceItems: Omit<InvoiceItemInfo, 'itemId' | 'invoiceId' | 'createBy' | 'createTime'>[]
invoiceStatus?: number
taxRate: number
dataSource?: string
invoiceItems: InvoiceItemAddReq[]
}
export interface InvoiceUpdateReq {
invoiceId: number
invoiceDate?: string
invoiceNo: string
orderNo: string
deliveryNo: string
dealerCode: string
dealerName: string
totalAmount: number
invoiceDate: string
invoiceStatus?: number
invoiceNo?: string
invoiceItems?: Omit<InvoiceItemInfo, 'itemId' | 'invoiceId' | 'createBy' | 'createTime'>[]
taxRate: number
dataSource?: string
invoiceItems: InvoiceItemUpdateReq[]
}
export interface InvoiceItemAddReq {
productCode: string
productName: string
productQty: number
unitPrice: number
itemAmount: number
taxRate: number
taxAmount: number
}
export interface InvoiceItemUpdateReq {
itemId?: number
productCode: string
productName: string
productQty: number
unitPrice: number
itemAmount: number
taxRate: number
taxAmount: number
}
// 发票API接口
export const invoiceApi = {
// 分页查询发票列表
getInvoiceList: (params: InvoiceQueryReq) => {
return request.get('/api/invoice/list', { params })
return request.get('/api/invoice/list', params)
},
// 获取发票详情
getInvoiceDetail: (invoiceId: number) => {
return request.get(`/api/invoice/${invoiceId}`)
},
// 新增发票
addInvoice: (data: InvoiceAddReq) => {
return request.post('/api/invoice/add', data)
return request.post('/api/invoice', data)
},
// 更新发票
updateInvoice: (data: InvoiceUpdateReq) => {
return request.post('/api/invoice/update', data)
},
// 删除发票
deleteInvoice: (invoiceId: number) => {
return request.delete(`/api/invoice/${invoiceId}`)
},
// 批量删除发票
batchDeleteInvoice: (invoiceIds: number[]) => {
batchDeleteInvoices: (invoiceIds: number[]) => {
return request.post('/api/invoice/batchDelete', invoiceIds)
},
// 导出发票数据
exportInvoice: (params: InvoiceQueryReq) => {
return request.get('/api/invoice/export', { params, responseType: 'blob' })
updateInvoiceStatus: (invoiceId: number, invoiceStatus: number) => {
return request.post(`/api/invoice/${invoiceId}/invoiceStatus`, null, {
params: { invoiceStatus }
})
}
}
export default invoiceApi
......
......@@ -103,7 +103,7 @@ export interface OrderItemUpdateReq {
export const orderApi = {
// 分页查询订单列表
getOrderList: (params: OrderQueryReq) => {
return request.get('/order/list', { params })
return request.get('/order/list', params)
},
// 获取订单详情
......
......@@ -86,25 +86,25 @@
<table class="data-table">
<thead>
<tr>
<th>出库ID</th>
<th class="sortable">出库单号 ↕️</th>
<th>出库ID</th>
<th class="sortable">出库单号 ↕️</th>
<th>经销商编码</th>
<th>经销商名称</th>
<th>出库日期</th>
<th>出库金额</th>
<th>关联订单编号</th>
<th>出库状态</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="delivery in deliveryList" :key="delivery.deliveryId">
<td>{{ delivery.deliveryId }}</td>
<td>{{ delivery.deliveryNo }}</td>
<td>{{ delivery.dealerCode }}</td>
<td>{{ delivery.dealerName }}</td>
<td>{{ formatDate(delivery.deliveryDate) }}</td>
<td>{{ formatCurrency(delivery.totalAmount) }}</td>
<tr v-for="order in orderList" :key="order.deliveryId">
<td>{{ order.deliveryId }}</td>
<td>{{ order.deliveryNo }}</td>
<td>{{ order.dealerCode }}</td>
<td>{{ order.dealerName }}</td>
<td>{{ formatDate(order.deliveryDate) }}</td>
<td>{{ order.orderNo }}</td>
<td>
<span :class="getDeliveryStatusClass(delivery.deliveryStatus)">
{{ getDeliveryStatusText(delivery.deliveryStatus) }}
......@@ -113,6 +113,7 @@
<td>{{ formatTime(delivery.createTime || '') }}</td>
<td class="action-col">
<button @click="handleView(delivery)" class="view-btn">👁️ 查看</button>
<button @click="handleDelete(order)" class="delete-btn">🗑️ 删除</button>
</td>
</tr>
</tbody>
......@@ -298,6 +299,7 @@
v-model="addFormData.deliveryDate"
class="form-input"
type="datetime-local"
step="1"
required
/>
</div>
......@@ -409,7 +411,7 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { deliveryApi, type DeliveryInfo, type DeliveryQueryReq } from '../../api/delivery'
import { deliveryApi, type DeliveryAddReq, type DeliveryInfo, type DeliveryQueryReq } from '../../api/delivery'
// 响应式数据
const loading = ref(false)
......@@ -433,12 +435,14 @@ const addFormData = reactive({
// 搜索参数
const searchParams = reactive({
orderNo: '',
deliveryNo: '',
dealerCode: '',
dealerName: '',
deliveryStatus: undefined,
startDate: '',
endDate: '',
warehouseCode: '',
dataSource: '',
deliveryStartDate: '',
deliveryEndDate: '',
pageNum: 1,
pageSize: 10
})
......@@ -551,9 +555,9 @@ const handleReset = () => {
fetchDeliveries()
}
const handleView = async (delivery: DeliveryInfo) => {
const handleView = async (order: DeliveryInfo) => {
try {
const response = await deliveryApi.getDeliveryDetail(delivery.deliveryId) as any
const response = await deliveryApi.getDeliveryDetail(order.deliveryId) as any
console.log('出库查询详情API响应:', response)
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
......@@ -569,6 +573,19 @@ const handleView = async (delivery: DeliveryInfo) => {
}
}
const handleDelete = async (order: DeliveryInfo) => {
if (confirm(`确定要删除出库单"${order.deliveryNo}"吗?`)) {
try {
await deliveryApi.deleteDelivery(order.deliveryId)
showMessage('删除出库单成功', 'success')
fetchOrders() // 刷新列表
} catch (error) {
console.error('删除出库单失败:', error)
showMessage('删除出库单失败, 请重试', 'error')
}
}
}
const handleExport = () => {
showMessage('导出功能开发中...', 'warning')
......@@ -618,11 +635,36 @@ const calculateDeliveryItemTotal = (index: number) => {
const handleSubmitAdd = async () => {
try {
// 这里应该调用新增出库的API
// await deliveryApi.addDelivery(addFormData)
showMessage('新增出库功能开发中...', 'warning')
// 处理日期时间格式,将datetime-local格式转换为后端需要的格式
let formattedDeliveryDate = addFormData.deliveryDate
if (formattedDeliveryDate && formattedDeliveryDate.includes('T')) {
// 将 YYYY-MM-DDTHH:MM:SS 格式转换为 YYYY-MM-DD HH:MM:SS 格式
formattedDeliveryDate = formattedDeliveryDate.replace('T', ' ')
}
// 构造新增出库的请求数据
const deliveryData: DeliveryAddReq = {
deliveryNo: addFormData.deliveryNo,
dealerCode: addFormData.dealerCode,
dealerName: addFormData.dealerName,
orderNo: addFormData.orderNo,
deliveryDate: formattedDeliveryDate,
deliveryStatus: 1, // 默认已出库
warehouseCode: addFormData.warehouseCode,
dataSource: addFormData.dataSource || '系统录入',
deliveryItems: addFormData.deliveryItems.map(item => ({
productCode: item.productCode,
productName: item.productName,
deliveryQty: item.deliveryQty,
deliveryPrice: item.deliveryPrice,
deliveryAmount: item.deliveryAmount
}))
}
await deliveryApi.addDelivery(deliveryData)
showMessage('新增出库成功', 'success')
closeAddDialog()
// fetchOrders() // 刷新列表
fetchOrders() // 刷新列表
} catch (error) {
console.error('新增出库失败:', error)
showMessage('新增出库失败, 请重试', 'error')
......@@ -891,6 +933,11 @@ onMounted(() => {
color: white;
}
.delete-btn {
background: #dc3545;
color: white;
}
.status-btn {
background: #28a745;
color: white;
......
......@@ -4,6 +4,14 @@
<div class="search-section">
<div class="search-row">
<div class="search-item">
<label>发票编号:</label>
<input
v-model="searchParams.invoiceNo"
class="search-input"
placeholder="请输入发票编号"
/>
</div>
<div class="search-item">
<label>订单编号:</label>
<input
v-model="searchParams.orderNo"
......@@ -38,17 +46,17 @@
</div>
<div class="search-row">
<div class="search-item">
<label>开始日期:</label>
<label>开票开始日期:</label>
<input
v-model="searchParams.startDate"
v-model="searchParams.invoiceStartDate"
class="search-input"
type="date"
/>
</div>
<div class="search-item">
<label>结束日期:</label>
<label>开票结束日期:</label>
<input
v-model="searchParams.endDate"
v-model="searchParams.invoiceEndDate"
class="search-input"
type="date"
/>
......@@ -65,8 +73,6 @@
<div class="action-buttons">
<button @click="handleAdd" class="action-btn primary">✨ 新增发票</button>
<button @click="handleExport" class="action-btn secondary">📋 导出</button>
<button @click="handlePrint" class="action-btn secondary">🖨️ 打印</button>
<button @click="handleStatistics" class="action-btn secondary">📊 统计</button>
</div>
</div>
......@@ -88,40 +94,36 @@
<table class="data-table">
<thead>
<tr>
<th>订单ID</th>
<th class="sortable">订单编号 ↕️</th>
<th>发票ID</th>
<th class="sortable">发票编号 ↕️</th>
<th>经销商编码</th>
<th>经销商名称</th>
<th>订单日期</th>
<th>订单金额</th>
<th>开票日期</th>
<th>发票金额</th>
<th>开票状态</th>
<th>开票时间</th>
<th>发票号码</th>
<th>关联订单编号</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="order in orderList" :key="order.orderId">
<td>{{ order.orderId }}</td>
<td>{{ order.orderNo }}</td>
<tr v-for="order in orderList" :key="order.invoiceId">
<td>{{ order.invoiceId }}</td>
<td>{{ order.invoiceNo }}</td>
<td>{{ order.dealerCode }}</td>
<td>{{ order.dealerName }}</td>
<td>{{ formatDate(order.orderDate) }}</td>
<td>{{ formatDate(order.invoiceDate) }}</td>
<td>{{ formatCurrency(order.totalAmount) }}</td>
<td>
<span :class="getInvoiceStatusClass(order.invoiceStatus)">
{{ getInvoiceStatusText(order.invoiceStatus) }}
</span>
</td>
<td>{{ formatTime('') }}</td>
<td>{{ '-' }}</td>
<td>{{ formatTime(order.uploadTime || '') }}</td>
<td>{{ order.orderNo }}</td>
<td class="action-col">
<button @click="handleView(order)" class="view-btn">👁️ 查看</button>
<button @click="handleEdit(order)" class="edit-btn">✏️ 编辑</button>
<button @click="handleInvoiceStatus(order)" class="status-btn">
{{ order.invoiceStatus === 0 ? '开票' : '撤销开票' }}
</button>
<button @click="handlePrintInvoice(order)" class="print-btn">🖨️ 打印</button>
</td>
</tr>
</tbody>
......@@ -275,24 +277,47 @@
</div>
<div class="dialog-body">
<div v-if="invoiceDetail" class="detail-content">
<!-- 发票基本信息 -->
<div class="detail-section">
<h4>发票基本信息</h4>
<div class="detail-grid">
<div class="detail-item">
<label>订单编号:</label>
<span>{{ invoiceDetail.orderNo }}</span>
<label>发票ID:</label>
<span>{{ invoiceDetail.invoiceId }}</span>
</div>
<div class="detail-item">
<label>发票号码:</label>
<label>发票编号:</label>
<span>{{ invoiceDetail.invoiceNo }}</span>
</div>
<div class="detail-item">
<label>关联订单编号:</label>
<span>{{ invoiceDetail.orderNo }}</span>
</div>
<div class="detail-item">
<label>关联出库单编号:</label>
<span>{{ invoiceDetail.deliveryNo || '-' }}</span>
</div>
<div class="detail-item">
<label>经销商编码:</label>
<span>{{ invoiceDetail.dealerCode }}</span>
</div>
<div class="detail-item">
<label>经销商名称:</label>
<span>{{ invoiceDetail.dealerName }}</span>
</div>
<div class="detail-item">
<label>开票日期:</label>
<span>{{ formatDate(invoiceDetail.invoiceDate) }}</span>
</div>
<div class="detail-item">
<label>开票金额:</label>
<span>{{ formatCurrency(invoiceDetail.invoiceAmount) }}</span>
<label>开票状态:</label>
<span :class="getInvoiceStatusClass(invoiceDetail.invoiceStatus)">
{{ getInvoiceStatusText(invoiceDetail.invoiceStatus) }}
</span>
</div>
<div class="detail-item">
<label>发票总金额:</label>
<span class="amount-text">{{ formatCurrency(invoiceDetail.totalAmount) }}</span>
</div>
<div class="detail-item">
<label>税率:</label>
......@@ -300,19 +325,71 @@
</div>
<div class="detail-item">
<label>税额:</label>
<span>{{ formatCurrency(invoiceDetail.taxAmount) }}</span>
<span class="amount-text">{{ formatCurrency((invoiceDetail.totalAmount || 0) * (invoiceDetail.taxRate || 0)) }}</span>
</div>
<div class="detail-item">
<label>开票状态:</label>
<span :class="getInvoiceStatusClass(invoiceDetail.invoiceStatus)">
{{ getInvoiceStatusText(invoiceDetail.invoiceStatus) }}
</span>
<label>数据来源:</label>
<span>{{ invoiceDetail.dataSource || '-' }}</span>
</div>
<div class="detail-item">
<label>备注:</label>
<span>{{ invoiceDetail.remark || '-' }}</span>
<label>上传时间:</label>
<span>{{ formatTime(invoiceDetail.uploadTime || '') }}</span>
</div>
<div class="detail-item">
<label>创建时间:</label>
<span>{{ formatTime(invoiceDetail.createTime || '') }}</span>
</div>
<div class="detail-item">
<label>更新时间:</label>
<span>{{ formatTime(invoiceDetail.updateTime || '') }}</span>
</div>
</div>
</div>
<!-- 发票明细信息 -->
<div v-if="invoiceDetail.invoiceItems && invoiceDetail.invoiceItems.length > 0" class="detail-section">
<h4>发票明细信息</h4>
<div class="detail-table-container">
<table class="detail-table">
<thead>
<tr>
<th>产品编码</th>
<th>产品名称</th>
<th>数量</th>
<th>单价</th>
<th>金额</th>
<th>税率</th>
<th>税额</th>
</tr>
</thead>
<tbody>
<tr v-for="item in invoiceDetail.invoiceItems" :key="item.itemId">
<td>{{ item.productCode }}</td>
<td>{{ item.productName }}</td>
<td>{{ item.productQty }}</td>
<td>{{ formatCurrency(item.unitPrice) }}</td>
<td class="amount-text">{{ formatCurrency(item.itemAmount) }}</td>
<td>{{ item.taxRate ? (item.taxRate * 100).toFixed(2) + '%' : '-' }}</td>
<td class="amount-text">{{ formatCurrency(item.taxAmount || 0) }}</td>
</tr>
</tbody>
<tfoot>
<tr class="total-row">
<td colspan="4" class="total-label">合计:</td>
<td class="amount-text total-amount">{{ formatCurrency(invoiceDetail.totalAmount) }}</td>
<td>-</td>
<td class="amount-text total-amount">{{ formatCurrency((invoiceDetail.totalAmount || 0) * (invoiceDetail.taxRate || 0)) }}</td>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- 操作按钮 -->
<div class="detail-actions">
<button @click="handleEdit(invoiceDetail)" class="edit-btn">✏️ 编辑</button>
<button @click="handlePrintInvoice(invoiceDetail)" class="print-btn">🖨️ 打印</button>
<button @click="closeDetailDialog" class="close-btn">关闭</button>
</div>
</div>
</div>
......@@ -327,20 +404,23 @@ import { invoiceApi, type InvoiceInfo, type InvoiceQueryReq } from '../../api/in
// 响应式数据
const loading = ref(false)
const invoiceList = ref<InvoiceInfo[]>([])
const orderList = ref<InvoiceInfo[]>([])
const showDialog = ref(false)
const showDetailDialog = ref(false)
const isEdit = ref(false)
const invoiceDetail = ref<any>(null)
const invoiceDetail = ref<InvoiceInfo | null>(null)
// 搜索参数
const searchParams = reactive({
invoiceNo: '',
orderNo: '',
deliveryNo: '',
dealerCode: '',
dealerName: '',
invoiceStatus: undefined,
startDate: '',
endDate: '',
dataSource: '',
invoiceStartDate: '',
invoiceEndDate: '',
pageNum: 1,
pageSize: 10
})
......@@ -453,12 +533,15 @@ const handleSearch = () => {
const handleReset = () => {
Object.assign(searchParams, {
invoiceNo: '',
orderNo: '',
deliveryNo: '',
dealerCode: '',
dealerName: '',
invoiceStatus: undefined,
startDate: '',
endDate: ''
dataSource: '',
invoiceStartDate: '',
invoiceEndDate: ''
})
pagination.pageNum = 1
fetchOrders()
......@@ -478,12 +561,12 @@ const handleAdd = () => {
showDialog.value = true
}
const handleEdit = (order: OrderInfo) => {
const handleEdit = (order: InvoiceInfo) => {
isEdit.value = true
Object.assign(formData, {
orderNo: order.orderNo,
invoiceNo: (order as any).invoiceNo || '',
invoiceDate: (order as any).invoiceTime || '',
invoiceNo: order.invoiceNo,
invoiceDate: order.invoiceDate,
invoiceAmount: order.totalAmount,
taxRate: 0,
taxAmount: 0,
......@@ -492,13 +575,13 @@ const handleEdit = (order: OrderInfo) => {
showDialog.value = true
}
const handleView = async (order: OrderInfo) => {
const handleView = async (order: InvoiceInfo) => {
try {
const response = await invoiceApi.getInvoiceDetail(invoice.invoiceId) as any
const response = await invoiceApi.getInvoiceDetail(order.invoiceId) as any
console.log('发票管理详情API响应:', response)
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
if (response && response.orderId) {
if (response && response.invoiceId) {
invoiceDetail.value = response
showDetailDialog.value = true
} else {
......@@ -510,36 +593,16 @@ const handleView = async (order: OrderInfo) => {
}
}
const handleInvoiceStatus = async (order: OrderInfo) => {
const newStatus = order.invoiceStatus === 0 ? 1 : 0
const action = newStatus === 1 ? '开票' : '撤销开票'
if (confirm(`确定要${action}订单"${order.orderNo}"吗?`)) {
try {
await orderApi.updateInvoiceStatus(order.orderId, newStatus)
showMessage(`${action}成功`, 'success')
fetchOrders()
} catch (error) {
console.error(`${action}失败:`, error)
showMessage(`${action}失败, 请重试`, 'error')
}
}
}
const handleExport = () => {
showMessage('导出功能开发中...', 'warning')
}
const handlePrint = () => {
const handlePrintInvoice = (invoice: InvoiceInfo) => {
showMessage('打印功能开发中...', 'warning')
}
const handleStatistics = () => {
showMessage('统计功能开发中...', 'warning')
}
const handlePrintInvoice = (order: OrderInfo) => {
showMessage(`打印发票 ${order.orderNo} 功能开发中...`, 'warning')
}
const handleSubmit = async () => {
try {
......@@ -973,7 +1036,8 @@ onMounted(() => {
}
.dialog-content.large-dialog {
max-width: 1000px;
max-width: 1200px;
width: 95%;
}
.dialog-header {
......@@ -1105,4 +1169,145 @@ onMounted(() => {
color: #333;
font-size: 16px;
}
/* 发票详情样式 */
.detail-content {
padding: 20px;
}
.detail-section {
margin-bottom: 30px;
}
.detail-section h4 {
margin: 0 0 15px 0;
padding: 10px 0;
border-bottom: 2px solid #007bff;
color: #007bff;
font-size: 16px;
font-weight: 600;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
}
.detail-item {
display: flex;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.detail-item label {
font-weight: 600;
color: #333;
min-width: 120px;
margin-right: 10px;
}
.detail-item span {
color: #666;
flex: 1;
}
.amount-text {
color: #e74c3c;
font-weight: 600;
}
.detail-table-container {
margin-top: 15px;
overflow-x: auto;
}
.detail-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
.detail-table th,
.detail-table td {
padding: 10px 8px;
text-align: left;
border: 1px solid #ddd;
}
.detail-table th {
background: #f8f9fa;
font-weight: 600;
color: #333;
}
.detail-table tbody tr:nth-child(even) {
background: #f8f9fa;
}
.detail-table tbody tr:hover {
background: #e9ecef;
}
.total-row {
background: #e9ecef !important;
font-weight: 600;
}
.total-label {
text-align: right;
font-weight: 600;
}
.total-amount {
color: #e74c3c;
font-weight: 700;
font-size: 15px;
}
.detail-actions {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
display: flex;
gap: 10px;
justify-content: flex-end;
}
.detail-actions button {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.detail-actions .edit-btn {
background: #28a745;
color: white;
}
.detail-actions .edit-btn:hover {
background: #218838;
}
.detail-actions .print-btn {
background: #17a2b8;
color: white;
}
.detail-actions .print-btn:hover {
background: #138496;
}
.detail-actions .close-btn {
background: #6c757d;
color: white;
}
.detail-actions .close-btn:hover {
background: #5a6268;
}
</style>
......
......@@ -467,6 +467,8 @@ const searchParams = reactive<OrderQueryReq>({
invoiceStatus: undefined,
rebateCalcFlag: undefined,
verifyStatus: undefined,
minAmount: undefined,
maxAmount: undefined,
pageNum: 1,
pageSize: 10
})
......@@ -593,7 +595,20 @@ const showMessage = (message: string, type: 'success' | 'error' | 'warning' = 's
const fetchOrders = async () => {
try {
loading.value = true
const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize }
// 构造查询参数对象
const params: OrderQueryReq = {
orderNo: searchParams.orderNo || undefined,
dealerCode: searchParams.dealerCode || undefined,
dealerName: searchParams.dealerName || undefined,
deliveryStatus: searchParams.deliveryStatus,
invoiceStatus: searchParams.invoiceStatus,
rebateCalcFlag: searchParams.rebateCalcFlag,
verifyStatus: searchParams.verifyStatus,
minAmount: searchParams.minAmount,
maxAmount: searchParams.maxAmount,
pageNum: pagination.pageNum,
pageSize: pagination.pageSize
}
const response = await orderApi.getOrderList(params) as any
console.log('API响应:', response) // 添加调试日志
......@@ -632,7 +647,9 @@ const handleReset = () => {
deliveryStatus: undefined,
invoiceStatus: undefined,
rebateCalcFlag: undefined,
verifyStatus: undefined
verifyStatus: undefined,
minAmount: undefined,
maxAmount: undefined
})
pagination.pageNum = 1
fetchOrders()
......