@yqg/permission 1.2.1 → 1.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +50 -38
  2. package/dist/apply-modal-CpmDDWWV.js +12865 -0
  3. package/dist/category-selector-BQ0-kg3o.js +1275 -0
  4. package/dist/index-D_0ZQip-.js +2972 -0
  5. package/dist/index-wLkVeDMW.js +5040 -0
  6. package/dist/index.js +2 -2
  7. package/dist/permission-item-Df_aagL1.js +1266 -0
  8. package/dist/{yqg-permission-DfFns5kK.js → yqg-permission-BjVCs5lN.js} +3483 -3538
  9. package/dist/yqg-permission.umd.js +250 -0
  10. package/package.json +13 -3
  11. package/plugins/alioss.ts +237 -0
  12. package/src/App.vue +7 -8
  13. package/src/assets/category.png +0 -0
  14. package/src/axios/index.ts +6 -1
  15. package/src/components/apply-modal.vue +254 -157
  16. package/src/components/category-selector.vue +130 -0
  17. package/src/components/permission-item.vue +230 -0
  18. package/src/components/success-modal.vue +4 -4
  19. package/src/components/yqg-permission.vue +23 -110
  20. package/src/hooks/useAttributesCache.ts +21 -0
  21. package/src/hooks/useCategory.ts +20 -0
  22. package/src/hooks/useDragable.ts +9 -10
  23. package/src/hooks/useFormat.ts +56 -0
  24. package/src/hooks/useStatus.ts +82 -0
  25. package/src/i18n/zh-CH.ts +9 -2
  26. package/src/main.ts +2 -0
  27. package/src/typings/index.d.ts +35 -2
  28. package/src/utils/index.ts +9 -0
  29. package/src/yqg-permission/index.ts +13 -1
  30. package/vite.config.ts +18 -2
  31. package/dist/apply-modal-COwJCSGK.js +0 -8742
  32. package/dist/checkbox-item-DyKSHMQJ.js +0 -4991
  33. package/dist/index-DKDl-l25.js +0 -6164
  34. package/dist/index.umd.cjs +0 -259
  35. package/src/assets/apply.png +0 -0
  36. package/src/components/checkbox-item.vue +0 -201
@@ -1,182 +1,279 @@
1
1
  <template>
2
- <Modal
3
- v-model:open="open"
4
- :title="t('permissionApply')"
5
- width="800px"
6
- @ok="handleOk"
7
- :okText="t('submit')"
8
- :maskClosable="false"
9
- :ok-button-props="{ loading: loading }"
10
- :cancelText="t('cancel')">
11
- <Form
12
- ref="formRef"
13
- :model="formState"
14
- :labelCol="{ span: 4 }"
15
- :wrapperCol="{ span: 19 }">
16
-
17
- <FormItem
18
- :label="t('applyPermission')"
19
- name="roleIds"
20
- :rules="[{ required: true, message: t('selectPlaceholder')}]">
21
- <span v-if="!permissionList.length">
22
- {{t('noPermissionTips')}}
23
- </span>
24
- <CheckboxGroup
25
- v-else
26
- v-model:value="formState.roleIds"
27
- style="display: block;"
28
- @change="onChangeHandler"
29
- >
30
- <CheckboxItem
31
- v-for="item in permissionList"
32
- :key="item.roleId"
33
- :item="item"
34
- :checkedIds="formState.roleIds"
35
- :onChangeTime="onChangeHandler">
36
- </CheckboxItem>
37
- </CheckboxGroup>
38
- </FormItem>
2
+ <Modal v-model:open="open" :title="t('permissionApply')" width="1100px" @ok="handleOk" :okText="t('submit')"
3
+ :maskClosable="false" :ok-button-props="{ loading: loading }" :cancelText="t('cancel')">
4
+ <Form ref="formRef" :model="formState" :labelCol="{ span: 4 }" :wrapperCol="{ span: 19 }">
5
+ <FormItem :label="t('applyPermission')" name="features"
6
+ :rules="[{ required: true, message: t('selectPlaceholder')}]">
7
+ <Spin :spinning="spining">
8
+ <span v-if="!permissionList.length">
9
+ {{t('noPermissionTips')}}
10
+ </span>
39
11
 
40
- <FormItem
41
- name="applyReason"
42
- :label="t('applyReason')"
43
- :rules="[{
44
- required: true, message: t('reasonPlaceholder'), trigger: ['change', 'blur']
45
- }, {
46
- max: 300, message: t('maxLengthTips', {length: 300}), trigger: ['change', 'blur']
47
- }]">
48
- <Textarea
49
- v-model:value.trim="formState.applyReason"
50
- :placeholder="t('applyReasonPlaceholder')"
51
- :auto-size="{ minRows: 4, maxRows: 4 }">
52
- </Textarea>
12
+ <Tree checkable :defaultExpandAll="true" :tree-data="permissionList" :height="200"
13
+ :checkedKeys="formState.features" class="crane-permission-tree" @check="onCheck">
14
+ <template #title="item: PermissionType">
15
+ <div v-if="item.children && item.children.length">
16
+ {{ item.shortName }}
17
+ </div>
18
+ <PermissionItem v-else :checkedKeys="formState.features" :item="item"
19
+ @onChangeTime="onChangeTime" @updateTime="setDefaultTime"
20
+ :validTimeOptions="validTimeOptions" />
21
+ </template>
22
+ </Tree>
23
+ <CategorySelector v-if="categoryList.length" :categoryList="categoryList" ref="categoryRef" />
24
+ </Spin>
25
+ </FormItem>
26
+ <FormItem name="applyReason" :label="t('applyReason')" :rules="[{
27
+ required: true, message: t('reasonPlaceholder'), trigger: ['change']
28
+ }, {
29
+ max: 300, message: t('maxLengthTips', {length: 300}), trigger: ['change', 'blur']
30
+ }]">
31
+ <Textarea v-model:value.trim="formState.applyReason" :placeholder="t('applyReasonPlaceholder')"
32
+ :auto-size="{ minRows: 4, maxRows: 4 }">
33
+ </Textarea>
53
34
  <span class="reason-tips" style="font-size: 12px">
54
35
  {{t('applyReasonTips')}}
55
36
  </span>
56
37
  </FormItem>
57
38
 
58
39
  <FormItem :label="t('approvalProcess')">
59
- <ApprovalSteps :stepNodes="stepNodes"/>
40
+ <ApprovalSteps :stepNodes="stepNodes" />
60
41
  </FormItem>
61
42
  </Form>
62
43
 
63
- <SuccessModal ref="successModal"/>
44
+ <SuccessModal ref="successModal" />
64
45
 
65
- </Modal>
46
+ </Modal>
66
47
  </template>
67
48
  <script lang="ts" setup>
68
- import {
69
- reactive,
70
- defineAsyncComponent,
71
- toRef,
72
- ref,
73
- watch,
74
- } from 'vue';
75
- import Http from '../axios/index';
76
- import {
77
- Modal,
78
- Form,
79
- FormItem,
80
- CheckboxGroup,
81
- Textarea,
82
- message
83
- } from 'ant-design-vue';
84
- import SuccessModal from './success-modal.vue';
85
- import ApprovalSteps from './approval-steps.vue';
86
- import t, {throttle} from '../utils';
87
- const CheckboxItem = defineAsyncComponent(() =>
88
- import('./checkbox-item.vue')
89
- );
90
-
91
- const props = defineProps({
92
- permissionList: {
93
- type: Array as () => PermissionType[],
94
- default: () => []
95
- },
96
- businessCode: {
97
- type: String,
98
- default: ''
99
- },
100
- workNumber: {
101
- type: String,
102
- default: ''
103
- },
49
+ import {
50
+ reactive,
51
+ defineAsyncComponent,
52
+ toRef,
53
+ ref,
54
+ watch,
55
+ PropType,
56
+ computed
57
+ } from 'vue';
58
+ import Http from '../axios/index';
59
+ import {
60
+ Modal,
61
+ Form,
62
+ FormItem,
63
+ Textarea,
64
+ message,
65
+ Tree,
66
+ Spin
67
+ } from 'ant-design-vue';
68
+ import SuccessModal from './success-modal.vue';
69
+ import ApprovalSteps from './approval-steps.vue';
70
+ import t, {throttle, deepTree} from '../utils';
71
+ import useCategory from '../hooks/useCategory';
72
+
73
+ const CategorySelector = defineAsyncComponent(() =>
74
+ import('./category-selector.vue')
75
+ );
76
+ const PermissionItem = defineAsyncComponent(() =>
77
+ import('./permission-item.vue')
78
+ );
79
+
80
+ const props = defineProps({
81
+ permissionList: {
82
+ type: Array as PropType<any[]>,
83
+ default: () => []
84
+ },
85
+ workNumber: {
86
+ type: String,
87
+ default: ''
88
+ },
89
+ spining: {
90
+ type: Boolean,
91
+ default: false
92
+ }
93
+
94
+ });
95
+ const emit = defineEmits(['onSubmit', 'onSuccess']);
96
+
97
+ const open = defineModel({
98
+ required: true,
99
+ default: false,
100
+ });
101
+ const loading = ref(false);
102
+ const permissionList = computed(() => props.permissionList);
103
+ const submitWorkNumber = toRef(props, 'workNumber');
104
+ const successModal = ref<InstanceType<typeof SuccessModal>>();
105
+ const formRef = ref();
106
+ const categoryRef = ref();
107
+ let stepNodes = ref([]);
108
+ const categoryList = ref<CategoryType[]>([]);
109
+ const validTimeOptions = ref([]);
110
+ const formState = reactive<formStateType>({
111
+ features: [],
112
+ roleVoList: [],
113
+ applyReason: '',
114
+ dataRule: {
115
+ ruleItems: [],
116
+ },
117
+ submitWorkNumber: submitWorkNumber.value,
118
+ });
119
+
120
+ const getValidTimeOptions = async () => {
121
+ let res = await Http.getValidTimeOptions();
122
+ validTimeOptions.value = res.body;
123
+ };
124
+
125
+ getValidTimeOptions();
126
+ const handleOk = async() => {
127
+ await Promise.all([formRef.value.validate(), categoryRef.value?.validate()])
128
+ loading.value = true;
129
+ const params = getParams();
130
+ let res = await Http.submitApply(params);
131
+ const url = res?.body?.oaFlowUrl;
132
+ open.value = false;
133
+ loading.value = false;
134
+ emit('onSubmit');
135
+ successModal.value?.countDown(url, () => emit('onSuccess'));
136
+ };
137
+
138
+
139
+ const getParams = () => {
140
+ formState.submitWorkNumber = submitWorkNumber.value;
141
+ const roleVoList:{
142
+ roleId: number;
143
+ validTime: string;
144
+ }[] = [];
145
+
146
+ const ruleItems: { attributeCategoryId: number, attributeValueIds: number[]}[] = [];
147
+ categoryList.value.forEach((category: CategoryType) => {
148
+ // 添加有数据范围值的属性
149
+ if (category.attributeValueIds_view.length) {
150
+ ruleItems.push({
151
+ attributeCategoryId: category.id,
152
+ attributeValueIds: category.attributeValueIds_view
153
+ })
154
+ };
104
155
 
105
- });
106
- const emit = defineEmits(['onSubmit', 'onSuccess']);
107
- const open = defineModel({
108
- required: true,
109
- default: false,
110
- });
111
- const loading = ref(false);
112
- const permissionList = toRef(props, 'permissionList');
113
- const businessCode = toRef(props, 'businessCode');
114
- const submitWorkNumber = toRef(props, 'workNumber');
115
- const successModal = ref<InstanceType<typeof SuccessModal>>();
116
- const formRef = ref();
117
- let stepNodes = ref([]);
118
- const formState = reactive<formStateType>({
119
- applyBusinessCode: businessCode.value,
120
- roleIds: [],
121
- roleVoList: [],
122
- applyReason: '',
123
- submitWorkNumber: submitWorkNumber.value,
124
156
  });
125
157
 
126
- const handleOk = async() => {
127
- if (formState.roleIds.length > 5) {
128
- return message.warning(t('maxCountTips', {count: 5}));
158
+ deepTree(permissionList.value, (item: any) => {
159
+ if (!item.children && formState.features.includes(item.feature)) {
160
+ roleVoList.push({
161
+ roleId: item.roleId,
162
+ validTime: item.validTime
163
+ });
129
164
  }
130
- formRef.value.validate().then(async() => {
131
- loading.value = true;
132
- const params = getParams();
133
- let res = await Http.submitApply(params);
134
- const url = res?.body?.oaFlowUrl;
135
- open.value = false;
136
- loading.value = false;
137
- emit('onSubmit');
138
- successModal.value?.countDown(url, () => emit('onSuccess'));
139
- })
140
- };
141
-
142
- const getParams = () => {
143
- formState.roleVoList = formState.roleIds.map((roleId) => {
144
- return {
145
- roleId,
146
- validTime: permissionList.value.find((item) => item.roleId === roleId)?.validTime
147
- };
148
- });
149
- return formState;
150
- }
151
-
152
- const onChangeHandler = throttle(async () => {
153
- if (formState.roleIds.length > 5) {
154
- return;
165
+ })
166
+ formState.roleVoList = roleVoList
167
+ formState.dataRule.ruleItems = ruleItems;
168
+ return formState;
169
+ }
170
+
171
+ const setDefaultTime = (permission: PermissionType) => {
172
+ // 找到当前节点。设置时间
173
+ deepTree(permissionList.value, (item: any) => {
174
+ if (item.feature === permission.feature) {
175
+ item.validTime = permission.validTime;
155
176
  }
177
+ })
178
+ }
156
179
 
157
- if (!formState.roleIds.length) {
158
- stepNodes.value = [];
159
- return;
180
+ const onChangeTime = throttle(async () => {
181
+ const params = getParams();
182
+ if (params.roleVoList.length === 0) {
183
+ stepNodes.value = [];
184
+ return;
185
+ }
186
+ let res = await Http.getFlowPreview(params);
187
+ stepNodes.value = res?.body?.nodes || [];
188
+ }, 0)
189
+ const onCheck = (checkedIds:any, info:any) => {
190
+ // 如果选择的是子节点,判断是否超过5个,超过的话,删除当前选的节点,并给予提示
191
+ // 如果选择的是父节点,判断是否超过5个,超过的话,从当前选择的父节点的叶子节点中删除多出的个数,并给予提示
192
+ let total = 0;
193
+ let curTotal = 0;
194
+ deepTree(permissionList.value, (item: any) => {
195
+ if (!item.children && checkedIds.includes(item.feature)) {
196
+ total += 1;
160
197
  }
161
- const params = getParams();
162
- let res = await Http.getFlowPreview(params);
163
-
164
- stepNodes.value = res?.body?.nodes || [];
165
- }, 0)
166
-
167
- watch(() => formState.roleIds, (val) => {
168
- // 写到watch里避免闪烁
169
- if (val.length > 5) {
170
- formState.roleIds.pop();
171
- message.warning(t('maxCountTips', {count: 5}));
198
+ if (!item.children && formState.features.includes(item.feature)) {
199
+ curTotal += 1;
172
200
  }
173
201
  })
174
202
 
175
- watch(() => open.value, (cur) => {
176
- if (cur) {
177
- formRef.value?.resetFields();
178
- stepNodes.value = [];
179
- }
180
- })
203
+ if (curTotal === 5) {
204
+ message.warning(t('maxCountTips', {count: 5}));
205
+ return;
206
+ }
207
+ onChangeTime();
208
+ if (!info.node.children && total > 5) {
209
+ //选择的是子节点
210
+ formState.features = formState.features.filter((item) => item !== info.node.feature);
211
+ message.warning(t('maxCountTips', {count: 5}));
212
+ return;
213
+ }
214
+
215
+ if (info.node.children && total > 5) {
216
+ //选择的是父节点,则找出刚选的所有叶子节点, 并且删除其中多余(total-5)的节点
217
+ const diff = total - 5;
218
+ let leafNodes:string[] = [];
219
+ deepTree([info.node], (item: any) => {
220
+ if (!item.children && !formState.features.includes(item.feature)) {
221
+ leafNodes.push(item.feature);
222
+ }
223
+ })
224
+ // 从后面删除多余的节点
225
+ leafNodes = leafNodes.slice(0, leafNodes.length - diff);
226
+ formState.features = formState.features.concat(leafNodes);
227
+ message.warning(t('maxCountTips', {count: 5}));
228
+ return;
229
+ };
230
+
231
+ formState.features = checkedIds;
232
+
233
+ // 收集需要展示的数据属性
234
+ categoryList.value = useCategory(permissionList.value, formState.features);
235
+
236
+ // 预览审批流程
237
+ }
238
+
239
+ watch(() => open.value, (cur) => {
240
+ if (cur) {
241
+ formRef.value?.resetFields();
242
+ categoryList.value = [];
243
+ stepNodes.value = [];
244
+ }
245
+ })
246
+ </script>
247
+
248
+ <style scoped>
249
+ :deep(.yqg-permission-tree-list) {
250
+ margin-top: 4px;
251
+ }
252
+ :deep(.yqg-permission-tree-node-selected) {
253
+ background: none !important;
254
+ }
255
+
256
+ :deep(.yqg-permission-tree-checkbox) {
257
+ margin: 0 !important;
258
+ }
259
+
260
+ :deep(.yqg-permission-tree-switcher-noop) {
261
+ width: 18px !important;
262
+ }
263
+
264
+ :deep(.yqg-permission-tree-checkbox+span:hover) {
265
+ background: none !important;
266
+ }
267
+ :deep(.yqg-permission-tree-treenode) {
268
+ width: 100%!important;
269
+ }
270
+
271
+ :deep(.yqg-permission-tree-node-content-wrapper-normal) {
272
+ width: 100% !important;
273
+ }
274
+
275
+ :deep(.yqg-permission-tree-title) {
276
+ width: 100% !important;
277
+ }
181
278
 
182
- </script>
279
+ </style>
@@ -0,0 +1,130 @@
1
+ <template>
2
+ <Form class="crane-category-wraper" :model="categoryInfo" ref="formRef" v-if="showForm">
3
+ <div class="crane-required">{{ t('category') }}</div>
4
+ <div class="crane-category-tips">{{ t('categotySelectTips') }}</div>
5
+ <div class="crane-category-line"></div>
6
+ <template v-for="(item, index) in categoryInfo.categoryList" :key="item.id">
7
+ <FormItem :label="item.categoryName" :name="['categoryList', index, 'attributeValueIds_view']"
8
+ v-show="categoryValuesMap[item.id]?.length"
9
+ :rules="[{ required: true, message: t('pleaseChoose'), trigger: ['change', 'blur'] }]">
10
+ <TreeSelect treeCheckable treeDefaultExpandAll :tree-data="categoryValuesMap[item.id]" showSearch
11
+ allowClear v-model:value="item.attributeValueIds_view" treeNodeFilterProp="attributeName"
12
+ v-model:searchValue="searchValue" :fieldNames="{ label: 'attributeName', value: 'id' }"
13
+ treeNodeLabelProp="attributeName" :show-checked-strategy="SHOW_PARENT">
14
+ <template #title="{ attributeName }">
15
+ <span v-if="searchValue.toLowerCase() && attributeName.includes(searchValue.toLowerCase())"
16
+ style="color: #1677ff; font-weight: bold;">{{ attributeName
17
+ }}</span>
18
+ <span v-else>{{ attributeName }}</span>
19
+ </template>
20
+ </TreeSelect>
21
+ <span v-if="item.attributeValueIds_view?.includes(-1)" style="color: #ff4d4f; font-size: 12px;">
22
+ {{ t('categoryChangeTips') }}
23
+ </span>
24
+ </FormItem>
25
+ </template>
26
+ </Form>
27
+ </template>
28
+ <script lang="ts" setup>
29
+ import {
30
+ Form,
31
+ FormItem,
32
+ TreeSelect
33
+ } from 'ant-design-vue';
34
+
35
+ import { defineProps, PropType, ref, computed, defineExpose} from 'vue';
36
+ import t from '../utils';
37
+ import useAttributesCache from '../hooks/useAttributesCache';
38
+ const SHOW_PARENT = TreeSelect.SHOW_PARENT;
39
+
40
+ const props = defineProps({
41
+ categoryList: {
42
+ type: Array as PropType<CategoryType[]>,
43
+ default: () => []
44
+ },
45
+ })
46
+
47
+ const categoryInfo = computed(() => {
48
+ return {
49
+ categoryList: props.categoryList
50
+ }
51
+ })
52
+ const showForm = computed(() => {
53
+ // 判断是否有数据
54
+ return Object.values(categoryValuesMap).some(item => item.length > 0);
55
+ });
56
+ const formRef = ref();
57
+ const searchValue = ref('');
58
+ const { categoryValuesMap } = useAttributesCache(props.categoryList);
59
+
60
+ // 返回promise
61
+ const validate = () => {
62
+ return new Promise((resolve, reject) => {
63
+ formRef.value.validate().then(() => {
64
+ resolve(true);
65
+ }).catch(() => {
66
+ reject(false);
67
+ });
68
+ });
69
+ }
70
+
71
+ defineExpose({
72
+ validate
73
+ })
74
+
75
+ </script>
76
+
77
+ <style scoped>
78
+
79
+ .crane-category-wraper {
80
+ margin-top: 16px;
81
+ padding: 12px;
82
+ padding-bottom: 1px !important;
83
+ border-radius: 4px;
84
+ background-color: #F2F3F5;
85
+ }
86
+
87
+ :deep(.crane-category-wraper label) {
88
+ font-size: 12px !important;
89
+ }
90
+
91
+ :deep(.yqg-permission-form-item-explain-error) {
92
+ font-size: 12px;
93
+ }
94
+
95
+ :deep(.yqg-permission-form-item) {
96
+ margin-bottom: 18px;
97
+ }
98
+
99
+ :deep(.yqg-permission-form-item-required) {
100
+ font-size: 12px!important;
101
+ }
102
+
103
+ :deep(.yqg-permission-form-item-required::before) {
104
+ display: none!important;
105
+ }
106
+
107
+ .crane-required {
108
+ font-size: 14px;
109
+ font-weight: bold;
110
+ }
111
+
112
+ .crane-required::before {
113
+ display: inline-block;
114
+ margin-inline-end: 4px;
115
+ color: #ff4d4f;
116
+ font-size: 14px;
117
+ font-family: SimSun, sans-serif;
118
+ line-height: 1;
119
+ content: "*";
120
+ }
121
+ .crane-category-tips {
122
+ color: #86909C;
123
+ font-size: 12px;
124
+ }
125
+ .crane-category-line{
126
+ height: 1px;
127
+ background-color: #C9CDD4;
128
+ margin: 10px 0;
129
+ }
130
+ </style>