新建发票管理页面
This commit is contained in:
@@ -32,16 +32,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 未设置票据号码员工提示 -->
|
||||
<div v-if="showNoInvoiceWarning && employeesWithoutInvoice.length > 0" class="alert alert-warning warning-messages">
|
||||
<h4 class="warning-title">未设置票据号码提醒</h4>
|
||||
<p>以下员工尚未设置票据号码:</p>
|
||||
<ul class="warning-list">
|
||||
<li v-for="(employee, index) in employeesWithoutInvoice" :key="index" class="warning-item">
|
||||
{{ employee.name }} (员工工号: {{ employee.employeeId }})
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<div class="table-container">
|
||||
@@ -61,19 +52,28 @@
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(item, index) in filteredData"
|
||||
:key="item.id"
|
||||
:class="{ 'editing-row': item.isActive }"
|
||||
:key="item.keyId"
|
||||
:class="{'editing-row': item.isActive }"
|
||||
>
|
||||
<td class="sequence-number">{{ index + 1 }}</td>
|
||||
<td class="employee-info">
|
||||
<div class="input-container">
|
||||
<input
|
||||
<select
|
||||
v-if="item.isActive"
|
||||
v-model="item.operator"
|
||||
class="form-control"
|
||||
placeholder="请输入操作员名称"
|
||||
maxlength="20"
|
||||
/>
|
||||
class="form-control"
|
||||
>
|
||||
<option value="">请选择操作员</option>
|
||||
<option
|
||||
v-for="user in userList"
|
||||
:key="user.employeeId"
|
||||
:value="user.name"
|
||||
:data-employee-id="user.employeeId"
|
||||
@change="updateEmployeeId(item, user.employeeId)"
|
||||
>
|
||||
{{ user.name }}
|
||||
</option>
|
||||
</select>
|
||||
<span v-else class="employee-name">{{ item.operator || '-' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
@@ -140,7 +140,7 @@
|
||||
<td class="action-buttons">
|
||||
<button
|
||||
v-if="!item.isActive"
|
||||
@click="toggleEdit(item.id)"
|
||||
@click="toggleEdit(item.keyId)"
|
||||
class="btn btn-primary btn-sm mr-1"
|
||||
title="编辑"
|
||||
>
|
||||
@@ -148,14 +148,14 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="item.isActive"
|
||||
@click="toggleEdit(item.id)"
|
||||
@click="toggleEdit(item.keyId)"
|
||||
class="btn btn-secondary btn-sm mr-1"
|
||||
title="取消"
|
||||
>
|
||||
<i class="icon-cancel"></i> 取消
|
||||
</button>
|
||||
<button
|
||||
@click="deleteRow(item.id)"
|
||||
@click="deleteRow(item)"
|
||||
class="btn btn-danger btn-sm"
|
||||
title="删除"
|
||||
>
|
||||
@@ -189,71 +189,49 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listUser } from '@/api/system/user';
|
||||
import request from '@/utils/request'; // 导入请求工具
|
||||
|
||||
export default {
|
||||
name: 'InvoiceManagement',
|
||||
data() {
|
||||
return {
|
||||
// 用户信息和权限
|
||||
currentUser: {
|
||||
name: '骆丹颖',
|
||||
name: 'admin',
|
||||
employeeId: '1702',
|
||||
role: 'admin' // operator: 普通操作员, admin: 管理员
|
||||
},
|
||||
// 用户列表,用于操作员下拉选择(将在created中从后端获取)
|
||||
userList: [],
|
||||
saveButtonText: '保存',
|
||||
// 未设置票据号码的员工信息
|
||||
employeesWithoutInvoice: [],
|
||||
// 显示未设置票据号码提示
|
||||
showNoInvoiceWarning: false,
|
||||
|
||||
// 发票数据模型,增加更多必要字段
|
||||
invoiceData: [
|
||||
{
|
||||
id: 1,
|
||||
operator: '骆丹颖',
|
||||
employeeId: '1702',
|
||||
date: '2024-01-15',
|
||||
startNum: 'A20240001',
|
||||
endNum: 'A20240050',
|
||||
status: '已使用',
|
||||
currentNum: 'A20240050', // 当前使用到的号码
|
||||
isActive: false // 是否处于编辑状态
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
operator: '张明',
|
||||
employeeId: '1801',
|
||||
date: '2024-01-16',
|
||||
startNum: 'A20240051',
|
||||
endNum: 'A20240100',
|
||||
status: '已使用',
|
||||
currentNum: 'A20240100',
|
||||
isActive: false
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
operator: '李华',
|
||||
employeeId: '1903',
|
||||
date: '2024-01-17',
|
||||
startNum: 'A20240101',
|
||||
endNum: 'A20240150',
|
||||
status: '使用中',
|
||||
currentNum: 'A20240125',
|
||||
isActive: false
|
||||
}
|
||||
],
|
||||
invoiceData: [], // 初始为空数组,无默认数据
|
||||
// 表单验证相关状态
|
||||
validationErrors: [],
|
||||
validationErrors: [],
|
||||
// 过滤后显示的数据
|
||||
filteredData: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 计算属性:判断是否为管理员
|
||||
isAdmin() {
|
||||
return this.currentUser.role === 'admin';
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
// 初始化时根据用户权限过滤数据
|
||||
console.log('组件初始化,开始加载数据');
|
||||
// 从后端获取用户列表后再加载发票数据,确保用户信息可用
|
||||
this.getUserList().then(() => {
|
||||
console.log('用户列表加载完成,开始加载发票数据');
|
||||
this.loadInvoiceData();
|
||||
}).catch(() => {
|
||||
console.warn('用户列表加载失败,仍尝试加载发票数据');
|
||||
this.loadInvoiceData();
|
||||
});
|
||||
|
||||
// 初始化过滤数据,确保页面首次渲染时有数据显示
|
||||
this.filterDataByPermission();
|
||||
},
|
||||
|
||||
@@ -267,17 +245,183 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取用户列表
|
||||
getUserList() {
|
||||
return listUser().then((res) => {
|
||||
// 从响应中提取用户列表,并转换为需要的格式
|
||||
this.userList = res.data.records.map(user => ({
|
||||
name: user.nickName, // 使用用户昵称作为显示名称
|
||||
employeeId: user.userId, // 使用用户ID作为员工工号
|
||||
role: user.practitionerRolesDtoList?.some(role => role.roleKey === 'admin') ? 'admin' : 'operator'
|
||||
}));
|
||||
console.log('获取到的用户列表:', this.userList);
|
||||
return Promise.resolve();
|
||||
}).catch(error => {
|
||||
console.error('获取用户列表失败:', error);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
},
|
||||
// 从后端加载发票数据
|
||||
loadInvoiceData() {
|
||||
// 使用request工具从后端API获取发票段数据
|
||||
request({
|
||||
url: '/basicmanage/invoice-segment/page', // 更新为发票段的API路径
|
||||
method: 'get',
|
||||
params: {
|
||||
pageNo: 1,
|
||||
pageSize: 10000 // 进一步增大分页大小,确保能获取数据库中的所有记录
|
||||
}
|
||||
}).then(res => {
|
||||
console.log('获取到的发票段数据响应:', res);
|
||||
// 添加更多调试信息
|
||||
console.log('响应总记录数:', res.data.total || '未知');
|
||||
console.log('实际获取记录数:', res.data.records ? res.data.records.length : 0);
|
||||
|
||||
// 检查响应数据格式
|
||||
if (!res || !res.data) {
|
||||
console.error('响应数据格式不正确:', res);
|
||||
if (this.$message) {
|
||||
this.$message.error('获取发票数据失败:响应格式不正确');
|
||||
} else {
|
||||
alert('获取发票数据失败:响应格式不正确');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理响应数据,将后端adm_invoice_segment数据映射到前端所需的数据结构
|
||||
if (res.data.records && res.data.records.length > 0) {
|
||||
console.log(`成功获取到 ${res.data.records.length} 条发票段记录`);
|
||||
|
||||
this.invoiceData = res.data.records.map((record, index) => {
|
||||
const mappedRecord = {
|
||||
id: record.id, // 保留原始id
|
||||
segmentId: record.segmentId, // 保留原始segmentId
|
||||
// 为前端操作使用的标识符
|
||||
keyId: record.id || record.segmentId, // 用于前端组件的key
|
||||
// 更健壮的操作员名称获取逻辑
|
||||
// 更健壮的操作员名称获取逻辑,确保类型转换一致
|
||||
operator: (record.invoicingStaffName || record.employeeName) ?
|
||||
record.invoicingStaffName || record.employeeName :
|
||||
((record.invoicingStaffId || record.employeeId) ?
|
||||
this.getUserNameById(record.invoicingStaffId || record.employeeId) : '-'),
|
||||
employeeId: record.invoicingStaffId || record.employeeId || '',
|
||||
date: record.billBusDate || record.createDate ?
|
||||
new Date(record.billBusDate || record.createDate).toISOString().split('T')[0] : '',
|
||||
startNum: record.startNum || record.beginNumber || '',
|
||||
endNum: record.endNum || record.endNumber || '',
|
||||
// 从remark字段中提取currentNum值,支持多种格式
|
||||
currentNum: record.currentNum || record.currentNumber ||
|
||||
(record.remark && record.remark.includes('当前使用号码:') ?
|
||||
record.remark.split('当前使用号码:')[1].split(';')[0].trim() : ''),
|
||||
status: record.statusEnum ? record.statusEnum.value : record.status || '未使用',
|
||||
remark: record.remark || '', // 保留原始remark字段
|
||||
isActive: false // 确保初始状态不是编辑状态
|
||||
};
|
||||
|
||||
console.log(`记录 ${index + 1} 映射后的数据:`, mappedRecord);
|
||||
return mappedRecord;
|
||||
});
|
||||
|
||||
console.log('最终映射后的发票数据:', this.invoiceData);
|
||||
// 确保数据加载后立即过滤
|
||||
this.filterDataByPermission();
|
||||
} else {
|
||||
console.log('未获取到发票段记录或记录为空');
|
||||
this.invoiceData = [];
|
||||
this.filterDataByPermission(); // 即使没有数据也调用过滤函数
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取发票数据失败:', error);
|
||||
console.error('错误详情:', error.response || error);
|
||||
|
||||
// 显示更详细的错误信息
|
||||
let errorMessage = '加载发票数据失败';
|
||||
if (error.response) {
|
||||
errorMessage += `: ${error.response.status} ${error.response.statusText}`;
|
||||
} else if (error.message) {
|
||||
errorMessage += `: ${error.message}`;
|
||||
}
|
||||
|
||||
if (this.$message) {
|
||||
this.$message.error(errorMessage);
|
||||
} else {
|
||||
alert(errorMessage);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 更新发票数据到后端
|
||||
updateInvoiceData(record) {
|
||||
// 准备发送到后端的数据格式,适配adm_invoice_segment表结构
|
||||
const dataToSend = {
|
||||
id: record.id, // 保持一致的ID字段
|
||||
segmentId: record.segmentId || record.id, // 优先使用record.segmentId,确保segment_id不为空
|
||||
invoicingStaffId: record.employeeId,
|
||||
employeeId: record.employeeId, // 同时提供employeeId字段
|
||||
billBusDate: record.date ? new Date(record.date) : null,
|
||||
createDate: record.date ? new Date(record.date) : null,
|
||||
startNum: record.startNum,
|
||||
beginNumber: record.startNum, // 同时提供beginNumber字段
|
||||
endNum: record.endNum,
|
||||
endNumber: record.endNum, // 同时提供endNumber字段
|
||||
currentNum: record.currentNum,
|
||||
currentNumber: record.currentNum, // 同时提供currentNumber字段
|
||||
statusEnum: { value: record.status },
|
||||
status: record.status, // 同时提供status字段
|
||||
tenantId: 0, // 添加tenantId字段
|
||||
deleteFlag: '0' // 添加deleteFlag字段
|
||||
};
|
||||
|
||||
// 确保remark字段包含当前使用号码信息
|
||||
if (record.currentNum) {
|
||||
dataToSend.remark = `当前使用号码:${record.currentNum}`;
|
||||
}
|
||||
|
||||
console.log('准备更新发票段数据:', {
|
||||
url: '/basicmanage/invoice-segment/update',
|
||||
data: dataToSend
|
||||
});
|
||||
|
||||
// 调用后端API保存数据
|
||||
request({
|
||||
url: '/basicmanage/invoice-segment/update',
|
||||
method: 'post',
|
||||
data: dataToSend
|
||||
}).then(response => {
|
||||
console.log('发票段数据更新成功:', response);
|
||||
return response;
|
||||
}).catch(error => {
|
||||
console.error('发票段数据更新失败:', error);
|
||||
console.error('错误详情:', error.response || error);
|
||||
throw error;
|
||||
}).then(res => {
|
||||
console.log('更新发票数据成功:', res);
|
||||
}).catch(error => {
|
||||
console.error('更新发票数据失败:', error);
|
||||
if (this.$message) {
|
||||
this.$message.error('更新数据失败,请稍后重试');
|
||||
}
|
||||
});
|
||||
},
|
||||
// 根据用户权限过滤数据
|
||||
filterDataByPermission() {
|
||||
console.log('开始过滤数据,当前用户角色:', this.currentUser.role);
|
||||
console.log('过滤前数据总量:', this.invoiceData.length);
|
||||
|
||||
if (this.isAdmin) {
|
||||
// 管理员可以看到所有数据
|
||||
console.log('管理员模式,显示所有数据');
|
||||
this.filteredData = [...this.invoiceData];
|
||||
} else {
|
||||
// 普通操作员只能看到自己的数据
|
||||
// 普通操作员只能看到自己的数据,确保类型一致
|
||||
console.log('操作员模式,过滤条件:', this.currentUser.employeeId);
|
||||
const currentEmployeeId = String(this.currentUser.employeeId);
|
||||
this.filteredData = this.invoiceData.filter(item =>
|
||||
item.employeeId === this.currentUser.employeeId
|
||||
String(item.employeeId) === currentEmployeeId
|
||||
);
|
||||
}
|
||||
|
||||
console.log('过滤后显示的数据量:', this.filteredData.length);
|
||||
},
|
||||
|
||||
// 检查是否有权限修改指定记录
|
||||
@@ -295,9 +439,42 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 更新员工ID
|
||||
updateEmployeeId(item, employeeId) {
|
||||
item.employeeId = employeeId;
|
||||
},
|
||||
|
||||
// 根据员工ID获取用户名称
|
||||
getUserNameById(employeeId) {
|
||||
if (!employeeId) return '';
|
||||
const user = this.userList.find(u => String(u.employeeId) === String(employeeId));
|
||||
return user ? user.name : '';
|
||||
},
|
||||
|
||||
// 检查是否有权限修改指定记录
|
||||
canModifyRecord(record) {
|
||||
// 管理员可以修改所有记录,普通用户只能修改自己的
|
||||
return this.isAdmin || record.employeeId === this.currentUser.employeeId;
|
||||
},
|
||||
|
||||
// 检查权限并执行操作
|
||||
checkPermissionAndExecute(record, operation) {
|
||||
if (this.canModifyRecord(record)) {
|
||||
operation();
|
||||
} else {
|
||||
alert('您没有权限修改此记录!');
|
||||
}
|
||||
},
|
||||
|
||||
// 更新员工ID
|
||||
updateEmployeeId(item, employeeId) {
|
||||
item.employeeId = employeeId;
|
||||
},
|
||||
|
||||
addNewRow() {
|
||||
// 新增行时自动填充当前用户信息
|
||||
const newId = Math.max(...this.invoiceData.map(item => item.id), 0) + 1;
|
||||
// 使用负数作为临时ID,避免与后端数据库ID冲突
|
||||
const newId = -Math.max(...this.invoiceData.map(item => Math.abs(item.id)), 0) - 1;
|
||||
this.invoiceData.push({
|
||||
id: newId,
|
||||
operator: this.currentUser.name, // 自动使用当前用户名称
|
||||
@@ -307,12 +484,12 @@ export default {
|
||||
endNum: '',
|
||||
currentNum: '',
|
||||
status: '未使用',
|
||||
isActive: true // 新增行默认处于编辑状态
|
||||
isActive: true, // 新增行默认处于编辑状态
|
||||
isNewRecord: true // 添加标记表示这是新记录
|
||||
});
|
||||
},
|
||||
|
||||
deleteRow(id) {
|
||||
const record = this.invoiceData.find(item => item.id === id);
|
||||
deleteRow(record) {
|
||||
if (!record) {
|
||||
alert('未找到要删除的记录');
|
||||
return;
|
||||
@@ -333,28 +510,204 @@ export default {
|
||||
}
|
||||
|
||||
if (confirm('确定要删除这条记录吗?删除后无法恢复。')) {
|
||||
const index = this.invoiceData.findIndex(item => item.id === id);
|
||||
if (index > -1) {
|
||||
this.invoiceData.splice(index, 1);
|
||||
// 删除后刷新过滤数据
|
||||
this.filterDataByPermission();
|
||||
}
|
||||
// 添加详细的请求数据日志
|
||||
console.log('删除记录详情:', record);
|
||||
// 构建删除数据,优先使用id,如果没有则使用segmentId
|
||||
const idsToDelete = [];
|
||||
if (record.id) idsToDelete.push(record.id);
|
||||
if (record.segmentId && record.segmentId !== record.id) idsToDelete.push(record.segmentId);
|
||||
|
||||
// 如果没有有效的ID,尝试使用前端的keyId
|
||||
const deleteData = { ids: idsToDelete.length > 0 ? idsToDelete : [record.keyId] };
|
||||
console.log('准备发送删除请求:', { url: '/basicmanage/invoice-segment/delete', method: 'post', data: deleteData });
|
||||
// 更新日志以反映新的ID处理方式
|
||||
console.log('删除数据:', deleteData);
|
||||
console.log('删除ID类型检查:', deleteData.ids.map(id => ({value: id, type: typeof id})));
|
||||
|
||||
// 调用删除API
|
||||
console.log('开始删除操作,使用的ID:', deleteData.ids);
|
||||
console.log('请求数据:', deleteData);
|
||||
|
||||
request({
|
||||
url: '/basicmanage/invoice-segment/delete',
|
||||
method: 'post',
|
||||
data: deleteData
|
||||
}).then(res => {
|
||||
console.log('删除请求响应:', res);
|
||||
console.log('响应类型:', typeof res);
|
||||
console.log('响应结构:', JSON.stringify(res, null, 2));
|
||||
|
||||
// 处理响应 - 支持多种响应格式
|
||||
let isSuccess = false;
|
||||
let message = '';
|
||||
|
||||
// 检查是否为成功响应
|
||||
if (res.code === 200 || res.data?.code === 200) {
|
||||
isSuccess = true;
|
||||
message = res.msg || res.data?.msg || '删除成功';
|
||||
}
|
||||
// 检查是否有特定的成功标志
|
||||
else if (res.data?.success || res.success) {
|
||||
isSuccess = true;
|
||||
message = res.msg || res.data?.msg || '删除成功';
|
||||
}
|
||||
// 检查是否直接包含成功消息
|
||||
else if (res.msg?.includes('成功') || res.data?.msg?.includes('成功')) {
|
||||
isSuccess = true;
|
||||
message = res.msg || res.data?.msg || '删除成功';
|
||||
}
|
||||
// 其他情况视为失败
|
||||
else {
|
||||
message = res.msg || res.data?.msg || '删除失败';
|
||||
console.error('响应中未包含成功标志:', { code: res.code || res.data?.code, msg: message });
|
||||
}
|
||||
|
||||
// 检查是否在响应中包含影响行数信息
|
||||
let affectedRows = null;
|
||||
if (res.data?.affectedRows !== undefined) {
|
||||
affectedRows = res.data.affectedRows;
|
||||
} else if (res.affectedRows !== undefined) {
|
||||
affectedRows = res.affectedRows;
|
||||
}
|
||||
|
||||
console.log('删除操作影响行数:', affectedRows);
|
||||
|
||||
// 根据结果处理
|
||||
if (isSuccess) {
|
||||
// 特别处理影响行数为0的情况
|
||||
if (affectedRows === 0) {
|
||||
console.warn('删除操作成功但未影响任何记录,可能记录不存在或已被删除');
|
||||
|
||||
// 从前端数据中移除该记录(即使后端没有实际删除)
|
||||
const index = this.invoiceData.findIndex(item => item.keyId === record.keyId);
|
||||
if (index > -1) {
|
||||
this.invoiceData.splice(index, 1);
|
||||
this.filterDataByPermission();
|
||||
}
|
||||
|
||||
// 显示友好提示
|
||||
const notFoundMessage = '记录已不存在或已被删除,已从当前列表中移除';
|
||||
if (this.$message) {
|
||||
this.$message.warning(notFoundMessage);
|
||||
} else {
|
||||
alert(notFoundMessage);
|
||||
}
|
||||
} else {
|
||||
// 正常删除成功的情况
|
||||
const index = this.invoiceData.findIndex(item => item.keyId === record.keyId);
|
||||
if (index > -1) {
|
||||
this.invoiceData.splice(index, 1);
|
||||
// 删除后刷新过滤数据
|
||||
this.filterDataByPermission();
|
||||
}
|
||||
if (this.$message) {
|
||||
this.$message.success(message);
|
||||
} else {
|
||||
alert(message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 处理失败响应
|
||||
console.error('删除失败:', message, res);
|
||||
if (this.$message) {
|
||||
this.$message.error(message);
|
||||
} else {
|
||||
alert(message);
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
// 详细的错误处理
|
||||
console.error('删除请求异常捕获:', error);
|
||||
console.error('错误类型:', typeof error);
|
||||
console.error('完整错误对象:', error);
|
||||
|
||||
// 尝试获取更多错误信息
|
||||
if (error.response) {
|
||||
console.error('错误响应数据:', JSON.stringify(error.response, null, 2));
|
||||
} else if (error.config) {
|
||||
console.error('请求配置:', JSON.stringify(error.config, null, 2));
|
||||
}
|
||||
|
||||
// 智能错误消息提取
|
||||
let errorMessage = '删除失败';
|
||||
|
||||
// 1. 首先尝试从error对象的message属性获取(来自request.js的详细错误)
|
||||
if (error.message) {
|
||||
errorMessage = error.message;
|
||||
console.log('从error.message获取到错误:', errorMessage);
|
||||
}
|
||||
// 2. 然后检查是否有response对象,从中提取更详细的错误信息
|
||||
else if (error.response) {
|
||||
errorMessage = `${error.response.status} ${error.response.statusText}`;
|
||||
console.log('从error.response获取到状态:', errorMessage);
|
||||
if (error.response.data) {
|
||||
if (error.response.data.msg) {
|
||||
errorMessage = error.response.data.msg;
|
||||
} else if (error.response.data.message) {
|
||||
errorMessage = error.response.data.message;
|
||||
} else if (typeof error.response.data === 'string') {
|
||||
errorMessage = error.response.data;
|
||||
}
|
||||
console.log('从error.response.data获取到消息:', errorMessage);
|
||||
}
|
||||
}
|
||||
// 3. 最后如果是字符串错误,直接使用
|
||||
else if (typeof error === 'string') {
|
||||
errorMessage = error;
|
||||
console.log('直接使用字符串错误:', errorMessage);
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
console.error('最终确定的错误消息:', errorMessage);
|
||||
if (this.$message) {
|
||||
this.$message.error(errorMessage);
|
||||
} else {
|
||||
alert(errorMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 切换编辑状态
|
||||
toggleEdit(id) {
|
||||
const record = this.invoiceData.find(item => item.id === id);
|
||||
toggleEdit(keyId) {
|
||||
const record = this.invoiceData.find(item => item.keyId === keyId);
|
||||
if (!record) return;
|
||||
|
||||
this.checkPermissionAndExecute(record, () => {
|
||||
record.isActive = !record.isActive;
|
||||
// 如果取消编辑,清除错误信息
|
||||
if (!record.isActive) {
|
||||
this.validationErrors = [];
|
||||
// 判断是否是取消编辑操作
|
||||
if (record.isActive) {
|
||||
// 检查是否是未保存的新增行(通过isNewRecord标记判断)
|
||||
const isUnsavedNewRow = record.isNewRecord;
|
||||
|
||||
if (isUnsavedNewRow) {
|
||||
// 对于未保存的新增行,直接删除
|
||||
const index = this.invoiceData.findIndex(item => item.keyId === keyId);
|
||||
if (index > -1) {
|
||||
this.invoiceData.splice(index, 1);
|
||||
// 删除后刷新过滤数据
|
||||
this.filterDataByPermission();
|
||||
}
|
||||
} else {
|
||||
// 对于已保存的行,正常切换编辑状态
|
||||
record.isActive = false;
|
||||
// 清除错误信息
|
||||
this.validationErrors = [];
|
||||
}
|
||||
} else {
|
||||
// 进入编辑状态
|
||||
record.isActive = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
/* 测试用例:
|
||||
1. 点击新增按钮 -> 添加一行新记录(带有isNewRecord标记)
|
||||
2. 直接点击取消按钮 -> 应该删除该行,不保留任何痕迹
|
||||
3. 点击新增按钮 -> 添加一行新记录
|
||||
4. 输入一些数据后再点击取消按钮 -> 应该删除该行,不保留数据
|
||||
5. 点击新增按钮 -> 添加一行新记录
|
||||
6. 点击保存按钮 -> 保存成功后移除isNewRecord标记
|
||||
7. 再次点击编辑,然后点击取消 -> 应该只是退出编辑状态,不删除记录
|
||||
*/
|
||||
|
||||
// 发票号码使用后自动加1功能
|
||||
incrementInvoiceNumber(employeeId) {
|
||||
@@ -462,18 +815,21 @@ export default {
|
||||
},
|
||||
|
||||
// 从字符串末尾向前提取前缀,直到遇到第一个字母
|
||||
// 规则:前缀定义为从号码最末位开始往前推直到出现字母为止的前面字符,若无字母则无前缀
|
||||
extractPrefixFromEnd(str) {
|
||||
if (!str) return '';
|
||||
|
||||
// 从末尾向前遍历字符串
|
||||
for (let i = str.length - 1; i >= 0; i--) {
|
||||
// 如果遇到字母,返回从开始到该字母之前的部分
|
||||
// 例如:对于"ABC123",从末尾向前找到第一个字母'C',前缀就是'AB'
|
||||
// 对于"123ABC456",从末尾向前找到第一个字母'C',前缀就是"123AB"
|
||||
if (/[A-Za-z]/.test(str[i])) {
|
||||
return str.substring(0, i);
|
||||
}
|
||||
}
|
||||
// 如果没有找到字母,返回整个字符串
|
||||
return str;
|
||||
// 如果没有找到字母,则无前缀
|
||||
return '';
|
||||
},
|
||||
|
||||
// 验证发票号码格式
|
||||
@@ -532,8 +888,9 @@ export default {
|
||||
// 检查号码不重复(针对所有操作员)
|
||||
checkNumberUniqueness(record) {
|
||||
// 查找所有其他记录(不限操作员)
|
||||
// 使用keyId作为比较依据,因为它是前端唯一标识符
|
||||
const otherRecords = this.invoiceData.filter(
|
||||
item => item.id !== record.id
|
||||
item => item.keyId !== record.keyId
|
||||
);
|
||||
|
||||
for (const item of otherRecords) {
|
||||
@@ -574,7 +931,8 @@ export default {
|
||||
}
|
||||
|
||||
// 查找所有其他记录
|
||||
const otherRecords = this.invoiceData.filter(item => item.id !== record.id);
|
||||
// 使用keyId作为比较依据,因为它是前端唯一标识符
|
||||
const otherRecords = this.invoiceData.filter(item => item.keyId !== record.keyId);
|
||||
|
||||
// 提取数字部分进行比较的辅助函数
|
||||
const extractNumber = function(str) {
|
||||
@@ -673,29 +1031,35 @@ export default {
|
||||
},
|
||||
|
||||
// 验证单个记录
|
||||
// 测试用例说明:
|
||||
// 1. 测试起始和终止号码前缀一致性:例如"AB123"和"AB456"通过,"AB123"和"AC456"失败
|
||||
// 2. 测试当前号码与起始号码前缀一致性:例如"AB123"作为起始,"AB125"作为当前号码通过,"AC125"失败
|
||||
// 3. 测试无字母情况:纯数字"123456"作为起始、终止和当前号码都通过验证
|
||||
validateRecord(record) {
|
||||
const errors = [];
|
||||
// 使用keyId标识记录,避免使用可能不存在的id
|
||||
const recordIdentifier = record.keyId || record.id || '(新记录)';
|
||||
|
||||
// 验证操作员不为空
|
||||
if (!record.operator || !record.employeeId) {
|
||||
errors.push(`记录ID ${record.id}: 未设置票据号码的员工`);
|
||||
errors.push(`记录[${recordIdentifier}]: 未设置票据号码的员工`);
|
||||
}
|
||||
|
||||
// 验证起始号码
|
||||
const startNumValidation = this.validateInvoiceNumber(record.startNum);
|
||||
if (!startNumValidation.valid) {
|
||||
errors.push(`记录ID ${record.id}: 起始号码 ${startNumValidation.message}`);
|
||||
errors.push(`记录[${recordIdentifier}]: 起始号码 ${startNumValidation.message}`);
|
||||
}
|
||||
|
||||
// 验证终止号码
|
||||
const endNumValidation = this.validateInvoiceNumber(record.endNum);
|
||||
if (!endNumValidation.valid) {
|
||||
errors.push(`记录ID ${record.id}: 终止号码 ${endNumValidation.message}`);
|
||||
errors.push(`记录[${recordIdentifier}]: 终止号码 ${endNumValidation.message}`);
|
||||
}
|
||||
|
||||
// 验证起始和终止号码长度一致
|
||||
if (record.startNum && record.endNum && !this.validateNumberLengths(record.startNum, record.endNum)) {
|
||||
errors.push(`记录ID ${record.id}: 起始发票号码与终止发票号码长度必须完全一致`);
|
||||
errors.push(`记录[${recordIdentifier}]: 起始发票号码与终止发票号码长度必须完全一致`);
|
||||
}
|
||||
|
||||
// 验证起始和终止号码前缀一致性
|
||||
@@ -704,27 +1068,37 @@ export default {
|
||||
const endPrefix = this.extractPrefixFromEnd(record.endNum);
|
||||
|
||||
if (startPrefix !== endPrefix) {
|
||||
errors.push(`记录ID ${record.id}: 起始和终止号码前缀必须一致`);
|
||||
errors.push(`记录[${recordIdentifier}]: 起始和终止号码前缀必须一致(前缀定义:从号码最末位开始往前推直到出现字母为止的前面字符)`);
|
||||
}
|
||||
}
|
||||
|
||||
// 验证当前号码前缀与起始号码前缀一致性
|
||||
if (record.currentNum && record.startNum) {
|
||||
const currentPrefix = this.extractPrefixFromEnd(record.currentNum);
|
||||
const startPrefix = this.extractPrefixFromEnd(record.startNum);
|
||||
|
||||
if (currentPrefix !== startPrefix) {
|
||||
errors.push(`记录[${recordIdentifier}]: 当前使用号码前缀与起始号码前缀必须一致(前缀定义:从号码最末位开始往前推直到出现字母为止的前面字符)`);
|
||||
}
|
||||
}
|
||||
|
||||
// 验证当前号码在范围内
|
||||
if (record.currentNum && record.startNum && record.endNum &&
|
||||
!this.validateCurrentNumberInRange(record.currentNum, record.startNum, record.endNum)) {
|
||||
errors.push(`记录ID ${record.id}: 当前使用号码不在有效范围内`);
|
||||
errors.push(`记录[${recordIdentifier}]: 当前使用号码不在有效范围内`);
|
||||
}
|
||||
|
||||
// 验证号码唯一性
|
||||
const uniquenessCheck = this.checkNumberUniqueness(record);
|
||||
if (!uniquenessCheck.valid) {
|
||||
errors.push(`记录ID ${record.id}: ${uniquenessCheck.message}`);
|
||||
errors.push(`记录[${recordIdentifier}]: ${uniquenessCheck.message}`);
|
||||
}
|
||||
|
||||
// 检查号码范围是否与其他记录重叠
|
||||
if (record.startNum && record.endNum) {
|
||||
const overlapCheck = this.checkRangeOverlap(record);
|
||||
if (!overlapCheck.valid) {
|
||||
errors.push(`记录ID ${record.id}: ${overlapCheck.message}`);
|
||||
errors.push(`记录[${recordIdentifier}]: ${overlapCheck.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,53 +1137,133 @@ export default {
|
||||
|
||||
this.saveButtonText = '保存中...';
|
||||
|
||||
// 模拟保存操作
|
||||
setTimeout(() => {
|
||||
// 保存成功后将所有记录设为非编辑状态
|
||||
this.invoiceData.forEach(item => {
|
||||
item.isActive = false;
|
||||
// 准备要保存的记录列表
|
||||
const recordsToSave = this.invoiceData.filter(item => item.isActive || item.isNewRecord);
|
||||
|
||||
console.log(`准备保存 ${recordsToSave.length} 条记录`);
|
||||
|
||||
// 创建保存Promise数组
|
||||
const savePromises = recordsToSave.map((record, index) => {
|
||||
// 准备发送到后端的数据格式,适配adm_invoice_segment表结构
|
||||
const dataToSend = {
|
||||
// 对于更新操作,同时传递id和segmentId,确保后端能正确识别
|
||||
...(!record.isNewRecord && { id: record.keyId }),
|
||||
// 确保segmentId不为null,优先使用keyId,然后是segmentId,新记录时使用生成的临时ID
|
||||
segmentId: record.keyId || record.segmentId || Date.now(),
|
||||
// 员工和开票员信息
|
||||
employeeId: record.employeeId,
|
||||
employeeName: record.operator || this.getUserNameById(record.employeeId),
|
||||
invoicingStaffId: record.employeeId,
|
||||
invoicingStaffName: record.operator || this.getUserNameById(record.employeeId),
|
||||
// 日期信息
|
||||
billBusDate: record.date ? new Date(record.date) : null,
|
||||
createDate: record.date ? new Date(record.date) : null,
|
||||
// 号码信息,只使用数据库表对应的字段名
|
||||
beginNumber: record.startNum,
|
||||
endNumber: record.endNum,
|
||||
currentNumber: record.currentNum,
|
||||
// 状态信息
|
||||
status: record.status || '未使用',
|
||||
// 备注信息
|
||||
remark: record.currentNum ?
|
||||
(record.remark && !record.remark.includes('当前使用号码:') ?
|
||||
`${record.remark}; 当前使用号码:${record.currentNum}` :
|
||||
`当前使用号码:${record.currentNum}`
|
||||
) : (record.remark || ''),
|
||||
// 添加租户ID和删除标志
|
||||
tenantId: 1, // 假设默认租户ID为1
|
||||
deleteFlag: '0'
|
||||
};
|
||||
|
||||
// 添加调试信息,打印完整的记录对象
|
||||
console.log(`准备${record.isNewRecord ? '新增' : '更新'}的原始记录对象:`, {
|
||||
keyId: record.keyId,
|
||||
id: record.id,
|
||||
segmentId: record.segmentId,
|
||||
...record
|
||||
});
|
||||
|
||||
this.saveButtonText = '保存';
|
||||
alert('数据保存成功!');
|
||||
// 根据是否是新记录选择不同的API
|
||||
const url = record.isNewRecord ? '/basicmanage/invoice-segment/add' : '/basicmanage/invoice-segment/update';
|
||||
const operation = record.isNewRecord ? '新增' : '更新';
|
||||
|
||||
// 刷新未设置票据号码员工的警告提示
|
||||
this.refreshEmployeeWarnings();
|
||||
}, 800);
|
||||
console.log(`${operation}记录 ${index + 1}:`, {
|
||||
url: url,
|
||||
data: dataToSend
|
||||
});
|
||||
|
||||
return request({
|
||||
url: url,
|
||||
method: 'post',
|
||||
data: dataToSend
|
||||
}).then(response => {
|
||||
console.log(`${operation}记录 ${index + 1} 成功:`, response);
|
||||
return response;
|
||||
}).catch(err => {
|
||||
console.error(`${operation}记录 ${index + 1} 失败:`, err);
|
||||
throw err; // 重新抛出错误以便上层catch捕获
|
||||
});
|
||||
});
|
||||
|
||||
// 批量保存数据
|
||||
Promise.all(savePromises)
|
||||
.then(results => {
|
||||
console.log('批量保存发票数据成功:', results);
|
||||
|
||||
// 保存成功后将所有记录设为非编辑状态,并移除isNewRecord标记
|
||||
this.invoiceData.forEach(item => {
|
||||
item.isActive = false;
|
||||
// 移除新增记录标记,确保后续编辑再取消时不会被删除
|
||||
if (item.isNewRecord) {
|
||||
delete item.isNewRecord;
|
||||
}
|
||||
});
|
||||
|
||||
this.saveButtonText = '保存';
|
||||
|
||||
// 显示成功消息
|
||||
const successMessage = `成功保存 ${results.length} 条发票段记录!`;
|
||||
console.log(successMessage);
|
||||
|
||||
if (this.$message) {
|
||||
this.$message.success(successMessage);
|
||||
} else {
|
||||
alert(successMessage);
|
||||
}
|
||||
|
||||
// 重新加载数据以确保数据一致性
|
||||
console.log('保存成功后重新加载数据...');
|
||||
this.loadInvoiceData();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('保存发票数据失败:', error);
|
||||
console.error('错误详情:', error.response || error);
|
||||
this.saveButtonText = '保存';
|
||||
|
||||
// 显示错误消息
|
||||
let errorMessage = '保存数据失败';
|
||||
if (error.response) {
|
||||
errorMessage += `: ${error.response.status} ${error.response.statusText}`;
|
||||
} else if (error.message) {
|
||||
errorMessage += `: ${error.message}`;
|
||||
}
|
||||
|
||||
console.error(errorMessage);
|
||||
|
||||
if (this.$message) {
|
||||
this.$message.error(errorMessage);
|
||||
} else {
|
||||
alert(errorMessage);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 检查未设置票据号码的员工
|
||||
checkEmployeesWithoutInvoice() {
|
||||
// 模拟从API获取所有员工列表
|
||||
// 在实际应用中,应该从后端API获取所有员工信息
|
||||
const allEmployees = [
|
||||
{ name: '骆丹颖', employeeId: '1702' }
|
||||
];
|
||||
|
||||
// 提取已设置票据号码的员工工号
|
||||
const employeesWithInvoice = this.invoiceData.map(item => item.employeeId);
|
||||
|
||||
// 找出未设置票据号码的员工
|
||||
this.employeesWithoutInvoice = allEmployees.filter(employee =>
|
||||
!employeesWithInvoice.includes(employee.employeeId)
|
||||
);
|
||||
|
||||
// 只有当当前用户是管理员时才显示警告
|
||||
this.showNoInvoiceWarning = this.currentUser.role === 'admin' && this.employeesWithoutInvoice.length > 0;
|
||||
},
|
||||
|
||||
// 刷新未设置票据号码的员工列表
|
||||
refreshEmployeeWarnings() {
|
||||
this.checkEmployeesWithoutInvoice();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// 页面加载后初始化数据
|
||||
this.filterDataByPermission();
|
||||
|
||||
// 检查未设置票据号码的员工
|
||||
this.checkEmployeesWithoutInvoice();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -763,18 +763,27 @@ function handleExport() {
|
||||
);
|
||||
}
|
||||
/** 用户状态修改 */
|
||||
function handleStatusChange(row) {
|
||||
/* 测试用例:
|
||||
1. 启用状态切换:点击启用/停用开关,确认后只更新用户状态字段
|
||||
2. 角色保持不变:切换状态后,用户的角色权限应保持原样
|
||||
3. 不同用户类型:对管理员、普通用户、医生等不同角色用户测试状态切换
|
||||
4. 批量操作:测试批量修改状态是否也只改变状态不改变角色
|
||||
*/
|
||||
function handleStatusChange(row) {
|
||||
let text = row.status === '0' ? '启用' : '停用';
|
||||
proxy.$modal
|
||||
.confirm('确认要"' + text + '""' + row.userName + '"用户吗?')
|
||||
.then(function () {
|
||||
// 只更新用户状态,不修改用户角色
|
||||
return changeUserStatus(row.userId, row.status);
|
||||
})
|
||||
.then(() => {
|
||||
proxy.$modal.msgSuccess(text + '成功');
|
||||
getList(); // 刷新列表,确保显示最新的用户信息
|
||||
proxy.$modal.msgSuccess(text + '并设置为' + roleText + '成功');
|
||||
})
|
||||
.catch(function () {
|
||||
row.status = row.status === '0' ? '1' : '0';
|
||||
proxy.$modal.msgError('操作失败,请重试');
|
||||
});
|
||||
}
|
||||
/** 更多操作 */
|
||||
|
||||
Reference in New Issue
Block a user