刘光辉
4 小时以前 cf11e63b659afcc9792ab3e395f1b45d844431a8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<script lang="ts" setup>
import type { FileAction, FilePermissionDesignerContext, FilePermissionPolicy, FilePermissionRule } from '../../helper/filePermissions';
 
import { computed, ref } from 'vue';
 
import { buildUUID } from '@jnpf/utils';
 
import { Empty as AEmpty, Modal as AModal } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';
 
import { fileActions, filePermissionDesignerContext, isEditableFilePermissionPolicy, validateDesignerFilePermissions } from '../../helper/filePermissions';
 
const props = defineProps<{ bpmn: any; flowType: number; modelValue?: FilePermissionPolicy | null }>();
const emit = defineEmits<{ 'update:modelValue': [value: FilePermissionPolicy] }>();
const open = ref(false);
const policy = ref<FilePermissionPolicy>({ version: 1, rules: [] });
const errors = ref<string[]>([]);
const malformed = ref(false);
const context = ref<FilePermissionDesignerContext>({ attachments: [], nodes: [], fieldsByForm: {}, nodeIds: new Set() });
const count = computed(() => (Array.isArray(props.modelValue?.rules) ? props.modelValue.rules.length : 0));
const actionNames: Record<FileAction, string> = {
  VIEW: '查看',
  DOWNLOAD: '下载',
  EDIT: '在线编辑',
  REVIEW: '审阅(审批模式)',
  ADD: '上传新增',
  REPLACE: '替换附件',
  REMOVE: '移除附件',
};
const effects = [
  { value: 'INHERIT', label: '沿用原权限' },
  { value: 'ALLOW', label: '允许' },
  { value: 'DENY', label: '禁止' },
];
const subjects = [
  { value: 'CREATOR', label: '流程发起人' },
  { value: 'CURRENT_HANDLERS', label: '当前办理人' },
  { value: 'NON_CURRENT_HANDLERS', label: '非当前办理人' },
  { value: 'PARTICIPANTS', label: '本流程参与人' },
  { value: 'NODE_PARTICIPANTS', label: '指定节点参与人' },
];
const triggers = [
  { value: 'ACTIVE', label: '节点办理期间' },
  { value: 'ENTERED', label: '进入节点后持续生效' },
  { value: 'COMPLETED', label: '流程办结后' },
  { value: 'CANCELLED', label: '流程撤销后' },
  { value: 'TERMINATED', label: '流程终止后' },
];
const returns = [
  { value: 'KEEP', label: '退回后仍保留' },
  { value: 'RELEASE', label: '退回到触发节点前解除' },
];
function source() {
  return {
    data: props.bpmn.get('jnpfData').data,
    ids: new Set<string>(
      props.bpmn
        .get('elementRegistry')
        .getAll()
        .map((node: any) => node.id),
    ),
  };
}
function show() {
  const { data, ids } = source();
  context.value = filePermissionDesignerContext(data, ids);
  malformed.value = props.modelValue != null && !isEditableFilePermissionPolicy(props.modelValue);
  policy.value = malformed.value ? { version: 1, rules: [] } : cloneDeep(props.modelValue ?? { version: 1, rules: [] });
  errors.value = malformed.value ? ['现有文件权限配置结构无法编辑,请先核对配置;关闭窗口会保留原配置。'] : [];
  open.value = true;
}
function addRule() {
  policy.value.rules.push({
    id: buildUUID(),
    formId: '',
    field: '',
    subject: 'CREATOR',
    trigger: 'ACTIVE',
    actions: Object.fromEntries(fileActions.map((action) => [action, 'INHERIT'])),
  });
}
function targetValue(rule: FilePermissionRule) {
  return rule.formId && rule.field ? JSON.stringify([rule.formId, rule.field]) : undefined;
}
function selectTarget(rule: FilePermissionRule, value: string) {
  const target = context.value.attachments.find((item) => item.value === value);
  if (!target) return;
  rule.formId = target.formId;
  rule.field = target.field;
}
function changeTrigger(rule: FilePermissionRule) {
  if (!['ACTIVE', 'ENTERED'].includes(rule.trigger)) delete rule.triggerNodeId;
  if (rule.trigger !== 'ENTERED') delete rule.onReturn;
}
function submit() {
  if (malformed.value) return;
  const { data, ids } = source();
  context.value = filePermissionDesignerContext(data, ids);
  const candidate = { ...data, global: { ...data.global, filePermissionPolicy: policy.value } };
  errors.value = validateDesignerFilePermissions(candidate, ids, props.flowType);
  if (errors.value.length) return;
  // 无配置的老流程仅打开/确认窗口,不凭空新增策略。
  if (props.modelValue != null || policy.value.rules.length) emit('update:modelValue', cloneDeep(policy.value));
  open.value = false;
}
</script>
 
<template>
  <a-button class="w-full" :disabled="![0, 1].includes(flowType)" @click="show">文件权限设置{{ count ? `(${count} 条)` : '' }}</a-button>
  <div v-if="![0, 1].includes(flowType)" class="mt-[8px] text-gray-500">当前流程类型暂不支持文件权限规则</div>
  <AModal v-model:open="open" title="流程文件权限" :width="880" :ok-button-props="{ disabled: malformed }" :mask-closable="false" @ok="submit">
    <a-alert type="info" show-icon message="规则只影响使用该版本新建的本流程实例。未配置动作沿用原权限;禁止优先,允许不能突破表单和控件原有权限。" />
    <div class="my-[12px] text-gray-500">按附件字段设置作用人员与生效条件。子表规则适用于该字段各行;已有流程实例不随配置修改。</div>
    <div class="mb-[12px] text-gray-500">表单首次保存按原有上传权限保存初始附件;保存成功后,后续附件操作按本流程实例的规则控制。</div>
    <a-alert v-for="(error, index) in errors" :key="index" class="mb-[8px]" type="error" show-icon :message="error" />
    <template v-if="!malformed">
      <AEmpty v-if="!policy.rules.length" description="尚未配置文件权限规则,沿用原权限" />
      <a-card v-for="(rule, index) in policy.rules" :key="rule.id" :title="`规则 ${index + 1}`" size="small" class="mb-[12px]">
        <template #extra><a-button type="link" danger @click="policy.rules.splice(index, 1)">删除规则</a-button></template>
        <a-form layout="vertical">
          <a-form-item label="附件字段" required>
            <a-select
              :value="targetValue(rule)"
              :options="context.attachments"
              placeholder="请选择本流程的附件字段"
              @change="(value) => selectTarget(rule, value)" />
          </a-form-item>
          <div class="grid grid-cols-2 gap-x-[16px]">
            <a-form-item label="作用人员" required>
              <a-select v-model:value="rule.subject" :options="subjects" @change="rule.subject !== 'NODE_PARTICIPANTS' && delete rule.subjectNodeId" />
            </a-form-item>
            <a-form-item v-if="rule.subject === 'NODE_PARTICIPANTS'" label="参与人所属节点" required>
              <a-select v-model:value="rule.subjectNodeId" :options="context.nodes" placeholder="请选择节点" />
            </a-form-item>
            <a-form-item label="生效条件" required>
              <a-select v-model:value="rule.trigger" :options="triggers" @change="changeTrigger(rule)" />
            </a-form-item>
            <a-form-item v-if="['ACTIVE', 'ENTERED'].includes(rule.trigger)" label="触发节点" required>
              <a-select v-model:value="rule.triggerNodeId" :options="context.nodes" placeholder="请选择节点" />
            </a-form-item>
            <a-form-item v-if="rule.trigger === 'ENTERED'" label="退回后的处理" required>
              <a-select v-model:value="rule.onReturn" :options="returns" placeholder="请选择退回后的处理" />
            </a-form-item>
          </div>
          <div class="grid grid-cols-3 gap-x-[16px]">
            <a-form-item v-for="action in fileActions" :key="action" :label="actionNames[action]">
              <a-select :value="rule.actions[action] ?? 'INHERIT'" :options="effects" @change="(value) => (rule.actions[action] = value)" />
            </a-form-item>
          </div>
        </a-form>
      </a-card>
      <a-button type="dashed" block :disabled="!context.attachments.length" @click="addRule">添加文件权限规则</a-button>
      <div v-if="!context.attachments.length" class="mt-[8px] text-gray-500">请先在节点中选择包含文件上传控件的表单。</div>
    </template>
  </AModal>
</template>