feat(oa/crm): 实现出差申请交流反馈权限控制

- 添加用户角色列表管理功能
- 实现基于角色的交流过程与反馈字段可见性控制
- 集成tripCommView角色权限验证机制
- 支持申请人本人查看反馈内容
- 更新表格列显示逻辑增加角色权限判断
- 移除固定用户ID访问限制改为角色化控制
- 添加行程明细列表和创建人字段类型定义
- 注释掉无权限用户的反馈填写功能入口
dev
Yangk 2 days ago
parent cdc76abc52
commit 51e1d51f27

@ -117,6 +117,16 @@ export interface BusinessTripApplyVO {
* ID * ID
*/ */
ossId: string | number; ossId: string | number;
/**
*
*/
createBy?: string | number;
/**
*
*/
exchangeFeedback?: string;
} }
export interface BusinessTripApplyForm extends BaseEntity { export interface BusinessTripApplyForm extends BaseEntity {
@ -268,6 +278,16 @@ export interface BusinessTripApplyForm extends BaseEntity {
* ID * ID
*/ */
copyUserIds?: string | string[]; copyUserIds?: string | string[];
/**
*
*/
exchangeFeedback?: string;
/**
*
*/
crmBusinessTripDetailsList?: any[];
} }
export interface BusinessTripApplyQuery extends PageQuery { export interface BusinessTripApplyQuery extends PageQuery {

@ -432,30 +432,15 @@ const submitFormData = ref<StartProcessBo>({
}); });
const taskVariables = ref<Record<string, any>>({}); const taskVariables = ref<Record<string, any>>({});
// ID //
const BUSINESS_DIRECTION_LEADER_MAP: Record<number, string> = { const userRoles = ref<string[]>([]);
1: '1985254821554475009', // ""
2: '1985258519835889666', const hasTripCommViewRole = computed(() => userRoles.value?.some((role) => role === 'tripCommView'));
3: '1985251968270127105', //
4: '1985257496048226305',
5: '1985254145713688578'
};
//
const canViewFeedback = computed(() => { const canViewFeedback = computed(() => {
if (hasTripCommViewRole.value) return true;
const currentUserId = String(useUserStore().userId); const currentUserId = String(useUserStore().userId);
// 1.
if (form.value.applicantId && currentUserId === String(form.value.applicantId)) return true; if (form.value.applicantId && currentUserId === String(form.value.applicantId)) return true;
// 2. userList
const fixedNames = ['陈海军', '张东辉'];
const fixedIds = fixedNames
.map((name) => userList.value.find((u: any) => u.nickName === name))
.filter(Boolean)
.map((u: any) => String(u.userId));
if (fixedIds.includes(currentUserId)) return true;
// 3.
const bd = Number(form.value.businessDirection);
if (bd && BUSINESS_DIRECTION_LEADER_MAP[bd] && currentUserId === BUSINESS_DIRECTION_LEADER_MAP[bd]) return true;
return false; return false;
}); });
@ -591,19 +576,25 @@ onMounted(async () => {
const customerRes = await getCrmCustomerInfoList({}); const customerRes = await getCrmCustomerInfoList({});
customerList.value = customerRes.data || []; customerList.value = customerRes.data || [];
// //
if (!id) {
try { try {
const infoRes = await getInfo(); const infoRes = await getInfo();
userRoles.value = infoRes.data?.roles || [];
//
if (!id) {
if (infoRes.data?.user) { if (infoRes.data?.user) {
form.value.applicantId = infoRes.data.user.userId; form.value.applicantId = infoRes.data.user.userId;
form.value.applicantName = infoRes.data.user.nickName; form.value.applicantName = infoRes.data.user.nickName;
form.value.deptId = infoRes.data.user.deptId; form.value.deptId = infoRes.data.user.deptId;
form.value.deptName = infoRes.data.user.deptName; form.value.deptName = infoRes.data.user.deptName;
} }
}
} catch (e) { } catch (e) {
console.error('获取用户信息失败', e); console.error('获取用户信息失败', e);
userRoles.value = [];
} }
if (!id) {
// index tripType // index tripType
if (routeParams.value.tripType) { if (routeParams.value.tripType) {
form.value.tripType = routeParams.value.tripType; form.value.tripType = routeParams.value.tripType;
@ -728,7 +719,7 @@ const projectInfoSelectCallBack = (data: ProjectInfoVO[]) => {
} }
form.value.variables.approverId = String(selectedProject.managerId); form.value.variables.approverId = String(selectedProject.managerId);
handleApproverSelectChange(String(selectedProject.managerId)); handleApproverSelectChange(String(selectedProject.managerId));
proxy?.$modal.msgSuccess('已关联项目经理作为下一步审批人'); // proxy?.$modal.msgSuccess('');
} }
} }
} }

@ -120,7 +120,14 @@
</el-table-column> </el-table-column>
<el-table-column label="交流目的" align="center" prop="exchangePurpose" width="150" show-overflow-tooltip v-if="columns[17].visible" /> <el-table-column label="交流目的" align="center" prop="exchangePurpose" width="150" show-overflow-tooltip v-if="columns[17].visible" />
<el-table-column label="会议/展会名称" align="center" prop="meetingName" width="150" show-overflow-tooltip v-if="columns[19].visible" /> <el-table-column label="会议/展会名称" align="center" prop="meetingName" width="150" show-overflow-tooltip v-if="columns[19].visible" />
<el-table-column label="交流过程与反馈" align="center" prop="exchangeFeedback" width="200" show-overflow-tooltip v-if="columns[20].visible"> <el-table-column
label="交流过程与反馈"
align="center"
prop="exchangeFeedback"
width="200"
show-overflow-tooltip
v-if="columns[20].visible && hasTripCommViewRole"
>
<template #default="scope"> <template #default="scope">
<span v-if="canViewRowFeedback(scope.row)">{{ scope.row.exchangeFeedback }}</span> <span v-if="canViewRowFeedback(scope.row)">{{ scope.row.exchangeFeedback }}</span>
<span v-else style="color: #c0c4cc">-</span> <span v-else style="color: #c0c4cc">-</span>
@ -146,19 +153,19 @@
<el-tooltip content="查看详情" placement="top" v-if="scope.row.flowStatus !== 'draft' && scope.row.flowStatus"> <el-tooltip content="查看详情" placement="top" v-if="scope.row.flowStatus !== 'draft' && scope.row.flowStatus">
<el-button link type="info" icon="DocumentChecked" @click="handleView(scope.row)"></el-button> <el-button link type="info" icon="DocumentChecked" @click="handleView(scope.row)"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip <!-- <el-tooltip-->
content="填写反馈" <!-- content="填写反馈"-->
placement="top" <!-- placement="top"-->
v-if="scope.row.tripStatus === '3' && (scope.row.tripType === '2' || scope.row.tripType === '3') && canViewRowFeedback(scope.row)" <!-- v-if="scope.row.tripStatus === '3' && (scope.row.tripType === '2' || scope.row.tripType === '3') && canViewRowFeedback(scope.row)"-->
> <!-- >-->
<el-button <!-- <el-button-->
link <!-- link-->
type="success" <!-- type="success"-->
icon="ChatDotRound" <!-- icon="ChatDotRound"-->
@click="handleFeedback(scope.row)" <!-- @click="handleFeedback(scope.row)"-->
v-hasPermi="['oa/crm:businessTripApply:edit']" <!-- v-hasPermi="['oa/crm:businessTripApply:edit']"-->
></el-button> <!-- ></el-button>-->
</el-tooltip> <!-- </el-tooltip>-->
<!-- <el-tooltip content="删除" placement="top">--> <!-- <el-tooltip content="删除" placement="top">-->
<!-- <el-button--> <!-- <el-button-->
<!-- link--> <!-- link-->
@ -204,8 +211,21 @@ import { UserQuery, UserVO } from '@/api/system/user/types';
import { allListDept, listDept } from '@/api/system/dept'; import { allListDept, listDept } from '@/api/system/dept';
import { DeptVO } from '@/api/system/dept/types'; import { DeptVO } from '@/api/system/dept/types';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { getInfo } from '@/api/login';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userRoles = ref<string[]>([]);
// ""
const hasTripCommViewRole = computed(() => userRoles.value?.some((role) => role === 'tripCommView'));
//
const canViewRowFeedback = (row: BusinessTripApplyVO): boolean => {
if (hasTripCommViewRole.value) return true;
const currentUserId = String(useUserStore().userId);
if (row.createBy && currentUserId === String(row.createBy)) return true;
return false;
};
const { trip_status, trip_type, business_direction } = toRefs<any>(proxy?.useDict('trip_status', 'trip_type', 'business_direction')); const { trip_status, trip_type, business_direction } = toRefs<any>(proxy?.useDict('trip_status', 'trip_type', 'business_direction'));
const businessTripApplyList = ref<BusinessTripApplyVO[]>([]); const businessTripApplyList = ref<BusinessTripApplyVO[]>([]);
@ -219,32 +239,6 @@ const router = useRouter();
const userList = ref<UserVO[]>([]); const userList = ref<UserVO[]>([]);
const deptList = ref<DeptVO[]>([]); const deptList = ref<DeptVO[]>([]);
// ID
const BUSINESS_DIRECTION_LEADER_MAP: Record<number, string> = {
1: '1985254821554475009',
2: '1985258519835889666',
3: '1985251968270127105',
4: '1985257496048226305',
5: '1985254145713688578'
};
//
const canViewRowFeedback = (row: BusinessTripApplyVO): boolean => {
const currentUserId = String(useUserStore().userId);
// 1.
if (row.applicantId && currentUserId === String(row.applicantId)) return true;
// 2.
const fixedIds = ['陈海军', '张东辉']
.map(name => userList.value.find((u: any) => u.nickName === name))
.filter(Boolean)
.map((u: any) => String(u.userId));
if (fixedIds.includes(currentUserId)) return true;
// 3.
const bd = Number(row.businessDirection);
if (bd && BUSINESS_DIRECTION_LEADER_MAP[bd] && currentUserId === BUSINESS_DIRECTION_LEADER_MAP[bd]) return true;
return false;
};
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
// //
@ -380,6 +374,13 @@ const handleExport = () => {
}; };
onMounted(async () => { onMounted(async () => {
//
try {
const infoRes = await getInfo();
userRoles.value = infoRes.data?.roles || [];
} catch (e) {
userRoles.value = [];
}
// //
const userQuery = ref<UserQuery>(); const userQuery = ref<UserQuery>();
const userRes = await getUserList(userQuery.value); const userRes = await getUserList(userQuery.value);
@ -391,10 +392,6 @@ onMounted(async () => {
getList(); getList();
}); });
onActivated(() => {
getList();
});
// ============ ============ // ============ ============
const feedbackDialogVisible = ref(false); const feedbackDialogVisible = ref(false);
const feedbackLoading = ref(false); const feedbackLoading = ref(false);
@ -414,21 +411,21 @@ const feedbackRules = {
exchangeFeedback: [{ required: true, message: '请填写交流过程与反馈', trigger: 'blur' }] exchangeFeedback: [{ required: true, message: '请填写交流过程与反馈', trigger: 'blur' }]
}; };
/** 填写反馈按钮操作 */ // /** */
const handleFeedback = (row: BusinessTripApplyVO) => { // const handleFeedback = (row: BusinessTripApplyVO) => {
feedbackForm.value = { // feedbackForm.value = {
tripId: row.tripId, // tripId: row.tripId,
applyCode: row.applyCode, // applyCode: row.applyCode,
tripType: row.tripType, // tripType: row.tripType,
applicantId: row.applicantId, // applicantId: row.applicantId,
tripLocation: row.tripLocation, // tripLocation: row.tripLocation,
startTime: row.startTime, // startTime: row.startTime,
endTime: row.endTime, // endTime: row.endTime,
durationDays: row.durationDays, // durationDays: row.durationDays,
exchangeFeedback: row.exchangeFeedback || '' // exchangeFeedback: row.exchangeFeedback || ''
}; // };
feedbackDialogVisible.value = true; // feedbackDialogVisible.value = true;
}; // };
/** 提交反馈 */ /** 提交反馈 */
const submitFeedback = async () => { const submitFeedback = async () => {

Loading…
Cancel
Save