|
@@ -57,6 +57,10 @@ export class StageDeliveryComponent implements OnInit, OnDestroy {
|
|
|
@Input() customer: FmodeObject | null = null;
|
|
@Input() customer: FmodeObject | null = null;
|
|
|
@Input() currentUser: FmodeObject | null = null;
|
|
@Input() currentUser: FmodeObject | null = null;
|
|
|
@Input() canEdit: boolean = true;
|
|
@Input() canEdit: boolean = true;
|
|
|
|
|
+ // 组长权限标记(仅用于审批显示与操作)
|
|
|
|
|
+ isTeamLeader: boolean = false;
|
|
|
|
|
+ // 客服权限标记(仅从客服板块进入的项目)
|
|
|
|
|
+ isFromCustomerService: boolean = false;
|
|
|
|
|
|
|
|
// 路由参数
|
|
// 路由参数
|
|
|
cid: string = '';
|
|
cid: string = '';
|
|
@@ -211,10 +215,63 @@ export class StageDeliveryComponent implements OnInit, OnDestroy {
|
|
|
|
|
|
|
|
const role = this.currentUser?.get('roleName') || '';
|
|
const role = this.currentUser?.get('roleName') || '';
|
|
|
this.canEdit = ['客服', '组长', '管理员', '组员'].includes(role);
|
|
this.canEdit = ['客服', '组长', '管理员', '组员'].includes(role);
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 🔥 简化逻辑:URL参数是唯一判断标准 ==========
|
|
|
|
|
+ let isTeamLeaderEntry = false;
|
|
|
|
|
+
|
|
|
|
|
+ // 🔥 关键:只要URL有 roleName=team-leader 参数,就显示审批按钮
|
|
|
|
|
+ try {
|
|
|
|
|
+ const urlParams = new URLSearchParams(window?.location?.search || '');
|
|
|
|
|
+ const roleNameParam = urlParams.get('roleName');
|
|
|
|
|
+ if (roleNameParam === 'team-leader') {
|
|
|
|
|
+ isTeamLeaderEntry = true;
|
|
|
|
|
+ console.log('✅ 检测到URL参数 roleName=team-leader,强制启用审批按钮');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.log('❌ URL无 roleName=team-leader 参数,隐藏审批按钮');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('无法读取URL参数:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检测是否从客服端进入(用于显示其他功能)
|
|
|
|
|
+ let isCustomerServiceEntry = false;
|
|
|
|
|
+ if (!isTeamLeaderEntry) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const enterFromCS = localStorage.getItem('enterFromCustomerService') === '1';
|
|
|
|
|
+ const csMode = localStorage.getItem('customerServiceMode') === 'true';
|
|
|
|
|
+ if (enterFromCS || csMode) {
|
|
|
|
|
+ isCustomerServiceEntry = true;
|
|
|
|
|
+ console.log('✅ 检测到从客服板块进入');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('无法读取客服标记:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 最终判定 ==========
|
|
|
|
|
+ // 🔥 简化:URL参数是唯一判断标准,不检查用户角色
|
|
|
|
|
+ this.isTeamLeader = isTeamLeaderEntry;
|
|
|
|
|
+
|
|
|
|
|
+ // 从客服端进入时的标识
|
|
|
|
|
+ this.isFromCustomerService = isCustomerServiceEntry;
|
|
|
|
|
+
|
|
|
|
|
+ // 🔥 清除可能的冲突标记
|
|
|
|
|
+ if (isTeamLeaderEntry) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ localStorage.removeItem('enterFromCustomerService');
|
|
|
|
|
+ localStorage.removeItem('customerServiceMode');
|
|
|
|
|
+ console.log('🧹 已清除客服入口标记');
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('无法清除客服标记:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
console.log('✅ 用户信息:', {
|
|
console.log('✅ 用户信息:', {
|
|
|
name: this.currentUser?.get('name'),
|
|
name: this.currentUser?.get('name'),
|
|
|
role,
|
|
role,
|
|
|
- canEdit: this.canEdit
|
|
|
|
|
|
|
+ canEdit: this.canEdit,
|
|
|
|
|
+ isTeamLeader: this.isTeamLeader,
|
|
|
|
|
+ isFromCustomerService: this.isFromCustomerService
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -865,7 +922,7 @@ export class StageDeliveryComponent implements OnInit, OnDestroy {
|
|
|
this.saving = true;
|
|
this.saving = true;
|
|
|
this.cdr.markForCheck();
|
|
this.cdr.markForCheck();
|
|
|
|
|
|
|
|
- // 写入项目 data 的交付审批条目,供组长端看板识别
|
|
|
|
|
|
|
+ // 写入项目 data 的交付审批条目,供组长端看板识别(严格参考订单分配阶段)
|
|
|
const data = this.project.get('data') || {};
|
|
const data = this.project.get('data') || {};
|
|
|
const now = new Date();
|
|
const now = new Date();
|
|
|
const submitter = this.currentUser?.get('name') || '';
|
|
const submitter = this.currentUser?.get('name') || '';
|
|
@@ -887,6 +944,9 @@ export class StageDeliveryComponent implements OnInit, OnDestroy {
|
|
|
submittedById: submitterId
|
|
submittedById: submitterId
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // 关键:设置交付执行的审批状态为 pending(供页面显示审批条使用)
|
|
|
|
|
+ data.deliveryApprovalStatus = 'pending';
|
|
|
|
|
+
|
|
|
// 兼容:看板可能读取 pendingDeliveryApprovals
|
|
// 兼容:看板可能读取 pendingDeliveryApprovals
|
|
|
const pendingList = Array.isArray(data.pendingDeliveryApprovals) ? data.pendingDeliveryApprovals : [];
|
|
const pendingList = Array.isArray(data.pendingDeliveryApprovals) ? data.pendingDeliveryApprovals : [];
|
|
|
pendingList.push({
|
|
pendingList.push({
|
|
@@ -898,7 +958,11 @@ export class StageDeliveryComponent implements OnInit, OnDestroy {
|
|
|
});
|
|
});
|
|
|
data.pendingDeliveryApprovals = pendingList;
|
|
data.pendingDeliveryApprovals = pendingList;
|
|
|
|
|
|
|
|
|
|
+ // 同步顶层待审批标记,便于看板/统计读取
|
|
|
this.project.set('data', JSON.parse(JSON.stringify(data)));
|
|
this.project.set('data', JSON.parse(JSON.stringify(data)));
|
|
|
|
|
+ this.project.set('pendingApproval', true);
|
|
|
|
|
+ this.project.set('approvalStage', '交付执行');
|
|
|
|
|
+ this.project.set('lastDeliverySubmitTime', now);
|
|
|
await this.project.save();
|
|
await this.project.save();
|
|
|
|
|
|
|
|
// 发送通知
|
|
// 发送通知
|
|
@@ -982,4 +1046,275 @@ export class StageDeliveryComponent implements OnInit, OnDestroy {
|
|
|
const files = this.getProductDeliveryFiles(productId, typeId);
|
|
const files = this.getProductDeliveryFiles(productId, typeId);
|
|
|
return files.filter(f => f.approvalStatus === 'unverified').length;
|
|
return files.filter(f => f.approvalStatus === 'unverified').length;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取交付执行的审批状态(用于组长审批)
|
|
|
|
|
+ */
|
|
|
|
|
+ getDeliveryApprovalStatus(): 'pending' | 'approved' | 'rejected' | null {
|
|
|
|
|
+ if (!this.project) return null;
|
|
|
|
|
+ const data = this.project.get('data') || {};
|
|
|
|
|
+ const status = data.deliveryApprovalStatus || null;
|
|
|
|
|
+
|
|
|
|
|
+ // ✨ 增强调试日志 - 显示审批按钮的所有条件
|
|
|
|
|
+ console.log('🔍 【交付执行审批按钮显示条件检查】', {
|
|
|
|
|
+ '条件1_审批状态': status,
|
|
|
|
|
+ '条件1_是否pending': status === 'pending',
|
|
|
|
|
+ '条件2_isTeamLeader': this.isTeamLeader,
|
|
|
|
|
+ '条件3_isFromCustomerService': this.isFromCustomerService,
|
|
|
|
|
+ '条件3_非客服入口': !this.isFromCustomerService,
|
|
|
|
|
+ '✅ 所有条件满足': status === 'pending' && this.isTeamLeader && !this.isFromCustomerService,
|
|
|
|
|
+ '---详细信息---': '',
|
|
|
|
|
+ '用户角色': this.currentUser?.get('roleName'),
|
|
|
|
|
+ 'canEdit': this.canEdit,
|
|
|
|
|
+ 'data.deliveryApprovalStatus': data.deliveryApprovalStatus,
|
|
|
|
|
+ 'data.approvalHistory': data.approvalHistory?.length || 0
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return status;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 🔥 判断是否应该显示审批按钮(只有提交了审批才显示)
|
|
|
|
|
+ */
|
|
|
|
|
+ shouldShowApprovalButtons(): boolean {
|
|
|
|
|
+ if (!this.project) return false;
|
|
|
|
|
+
|
|
|
|
|
+ // 🔥 关键:只有在pending状态时才显示审批按钮
|
|
|
|
|
+ const status = this.getDeliveryApprovalStatus();
|
|
|
|
|
+ if (status !== 'pending') {
|
|
|
|
|
+ console.log('🔍 项目未处于待审批状态,隐藏审批按钮', { status });
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 组长从组长看板进入且项目处于待审批状态时,显示审批按钮
|
|
|
|
|
+ console.log('🔍 项目处于待审批状态,显示审批按钮');
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 🔥 判断是否有交付文件
|
|
|
|
|
+ */
|
|
|
|
|
+ hasDeliveryFiles(): boolean {
|
|
|
|
|
+ const totalFiles = this.getTotalFileCount();
|
|
|
|
|
+ console.log('🔍 交付文件数量:', totalFiles);
|
|
|
|
|
+ return totalFiles > 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取驳回原因
|
|
|
|
|
+ */
|
|
|
|
|
+ getDeliveryRejectionReason(): string {
|
|
|
|
|
+ if (!this.project) return '';
|
|
|
|
|
+ const data = this.project.get('data') || {};
|
|
|
|
|
+ return data.deliveryRejectionReason || '未填写';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 组长审批:通过交付执行
|
|
|
|
|
+ */
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 🔥 通过交付执行审批(支持未提交状态直接审批)
|
|
|
|
|
+ */
|
|
|
|
|
+ async approveDelivery(): Promise<void> {
|
|
|
|
|
+ if (!this.project || !this.currentUser || !this.isTeamLeader) {
|
|
|
|
|
+ console.warn('❌ 无法审批:缺少项目、用户或组长权限');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否有交付文件
|
|
|
|
|
+ if (!this.hasDeliveryFiles()) {
|
|
|
|
|
+ window?.fmode?.alert?.('项目暂无交付文件,无法审批');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确认审批
|
|
|
|
|
+ const confirmed = await window?.fmode?.confirm?.(
|
|
|
|
|
+ '确认通过交付执行审批?\n\n' +
|
|
|
|
|
+ '审批通过后,项目将进入下一阶段。'
|
|
|
|
|
+ );
|
|
|
|
|
+ if (!confirmed) return;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.saving = true;
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+
|
|
|
|
|
+ console.log('🔥 开始审批交付执行...');
|
|
|
|
|
+
|
|
|
|
|
+ const data = this.project.get('data') || {};
|
|
|
|
|
+ const now = new Date().toISOString();
|
|
|
|
|
+
|
|
|
|
|
+ // 设置审批状态
|
|
|
|
|
+ data.deliveryApprovalStatus = 'approved';
|
|
|
|
|
+ data.deliveryApprovedBy = this.currentUser.id;
|
|
|
|
|
+ data.deliveryApprovedByName = this.currentUser.get('name');
|
|
|
|
|
+ data.deliveryApprovedAt = now;
|
|
|
|
|
+
|
|
|
|
|
+ // 🔥 清除待审批标记(顶层字段)
|
|
|
|
|
+ this.project.set('pendingApproval', false);
|
|
|
|
|
+ this.project.set('approvalStage', null);
|
|
|
|
|
+
|
|
|
|
|
+ // 🔥 更新审批记录到 deliveryApproval
|
|
|
|
|
+ if (data.deliveryApproval) {
|
|
|
|
|
+ data.deliveryApproval.status = 'approved';
|
|
|
|
|
+ data.deliveryApproval.approvedBy = this.currentUser.id;
|
|
|
|
|
+ data.deliveryApproval.approvedByName = this.currentUser.get('name');
|
|
|
|
|
+ data.deliveryApproval.approvedAt = now;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.project.set('data', data);
|
|
|
|
|
+
|
|
|
|
|
+ console.log('💾 保存审批结果...');
|
|
|
|
|
+ await this.project.save();
|
|
|
|
|
+
|
|
|
|
|
+ console.log('✅ 交付执行审批通过!');
|
|
|
|
|
+ window?.fmode?.toast?.success?.('✅ 交付执行审批通过!');
|
|
|
|
|
+
|
|
|
|
|
+ // 刷新页面数据
|
|
|
|
|
+ await this.loadApprovalHistory();
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('❌ 审批失败:', error);
|
|
|
|
|
+ window?.fmode?.alert?.('审批失败,请重试');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.saving = false;
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 🔥 驳回交付执行(支持未提交状态直接驳回)
|
|
|
|
|
+ */
|
|
|
|
|
+ async rejectDelivery(): Promise<void> {
|
|
|
|
|
+ if (!this.project || !this.currentUser || !this.isTeamLeader) {
|
|
|
|
|
+ console.warn('❌ 无法驳回:缺少项目、用户或组长权限');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否有交付文件
|
|
|
|
|
+ if (!this.hasDeliveryFiles()) {
|
|
|
|
|
+ window?.fmode?.alert?.('项目暂无交付文件,无需驳回');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 输入驳回原因
|
|
|
|
|
+ const reason = await window?.fmode?.input?.('请输入驳回原因:\n\n驳回后设计师需要重新提交。');
|
|
|
|
|
+ if (!reason || reason.trim() === '') {
|
|
|
|
|
+ window?.fmode?.toast?.info?.('已取消驳回');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.saving = true;
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+
|
|
|
|
|
+ console.log('🔥 开始驳回交付执行...');
|
|
|
|
|
+
|
|
|
|
|
+ const data = this.project.get('data') || {};
|
|
|
|
|
+ const now = new Date().toISOString();
|
|
|
|
|
+
|
|
|
|
|
+ // 设置驳回状态
|
|
|
|
|
+ data.deliveryApprovalStatus = 'rejected';
|
|
|
|
|
+ data.deliveryRejectedBy = this.currentUser.id;
|
|
|
|
|
+ data.deliveryRejectedByName = this.currentUser.get('name');
|
|
|
|
|
+ data.deliveryRejectedAt = now;
|
|
|
|
|
+ data.deliveryRejectionReason = reason;
|
|
|
|
|
+
|
|
|
|
|
+ // 🔥 清除待审批标记(顶层字段)
|
|
|
|
|
+ this.project.set('pendingApproval', false);
|
|
|
|
|
+ this.project.set('approvalStage', null);
|
|
|
|
|
+
|
|
|
|
|
+ // 🔥 更新审批记录到 deliveryApproval
|
|
|
|
|
+ if (data.deliveryApproval) {
|
|
|
|
|
+ data.deliveryApproval.status = 'rejected';
|
|
|
|
|
+ data.deliveryApproval.rejectedBy = this.currentUser.id;
|
|
|
|
|
+ data.deliveryApproval.rejectedByName = this.currentUser.get('name');
|
|
|
|
|
+ data.deliveryApproval.rejectedAt = now;
|
|
|
|
|
+ data.deliveryApproval.rejectionReason = reason;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.project.set('data', data);
|
|
|
|
|
+
|
|
|
|
|
+ console.log('💾 保存驳回结果...');
|
|
|
|
|
+ await this.project.save();
|
|
|
|
|
+
|
|
|
|
|
+ console.log('✅ 已驳回交付执行');
|
|
|
|
|
+ window?.fmode?.toast?.success?.('✅ 已驳回交付执行,设计师需要重新提交');
|
|
|
|
|
+
|
|
|
|
|
+ // 刷新页面数据
|
|
|
|
|
+ await this.loadApprovalHistory();
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('❌ 驳回失败:', error);
|
|
|
|
|
+ window?.fmode?.alert?.('驳回失败,请重试');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.saving = false;
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 🧪 开发测试:快速标记交付执行为待审批状态
|
|
|
|
|
+ * 仅在组长端显示,方便测试审批流程
|
|
|
|
|
+ */
|
|
|
|
|
+ async markDeliveryAsPending(): Promise<void> {
|
|
|
|
|
+ if (!this.project || !this.isTeamLeader) {
|
|
|
|
|
+ console.warn('❌ 无法标记:缺少项目或非组长权限');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const confirmed = await window?.fmode?.confirm?.(
|
|
|
|
|
+ '🧪 测试功能\n\n' +
|
|
|
|
|
+ '这是一个开发测试功能,将会:\n' +
|
|
|
|
|
+ '1. 标记交付执行状态为"待审批"\n' +
|
|
|
|
|
+ '2. 清除之前的审批记录\n' +
|
|
|
|
|
+ '3. 显示审批按钮供测试\n\n' +
|
|
|
|
|
+ '是否继续?'
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (!confirmed) return;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.saving = true;
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+
|
|
|
|
|
+ console.log('🧪 开始标记交付执行为待审批...');
|
|
|
|
|
+
|
|
|
|
|
+ const data = this.project.get('data') || {};
|
|
|
|
|
+
|
|
|
|
|
+ // 设置为待审批状态
|
|
|
|
|
+ data.deliveryApprovalStatus = 'pending';
|
|
|
|
|
+
|
|
|
|
|
+ // 清除之前的审批记录
|
|
|
|
|
+ delete data.deliveryApprovedBy;
|
|
|
|
|
+ delete data.deliveryApprovedByName;
|
|
|
|
|
+ delete data.deliveryApprovedAt;
|
|
|
|
|
+ delete data.deliveryRejectedBy;
|
|
|
|
|
+ delete data.deliveryRejectedByName;
|
|
|
|
|
+ delete data.deliveryRejectedAt;
|
|
|
|
|
+ delete data.deliveryRejectionReason;
|
|
|
|
|
+
|
|
|
|
|
+ // 记录测试标记信息
|
|
|
|
|
+ data.testMarkedAt = new Date().toISOString();
|
|
|
|
|
+ data.testMarkedBy = this.currentUser?.get('name') || 'Unknown';
|
|
|
|
|
+
|
|
|
|
|
+ this.project.set('data', data);
|
|
|
|
|
+ await this.project.save();
|
|
|
|
|
+
|
|
|
|
|
+ console.log('✅ 交付执行已标记为待审批');
|
|
|
|
|
+ window?.fmode?.toast?.success?.('✅ 已标记为待审批,现在可以测试审批功能了');
|
|
|
|
|
+
|
|
|
|
|
+ // 刷新页面数据
|
|
|
|
|
+ await this.loadApprovalHistory();
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('❌ 标记失败:', error);
|
|
|
|
|
+ window?.fmode?.alert?.('标记失败,请检查控制台日志');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.saving = false;
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|