@yqg/permission 1.0.9 → 1.0.11-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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yqg/permission",
3
- "version": "1.0.9",
3
+ "version": "1.0.11-alpha.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "type": "module",
@@ -14,20 +14,21 @@
14
14
  "preview": "vite preview"
15
15
  },
16
16
  "devDependencies": {
17
- "@ant-design/icons-vue": "^7.0.1",
17
+ "@ant-design/icons-vue": "^7.0.1",
18
18
  "@commitlint/cli": "^19.5.0",
19
19
  "@commitlint/config-conventional": "^19.5.0",
20
- "ant-design-vue": "4.x",
21
- "axios": "^1.7.7",
22
- "vite-plugin-css-injected-by-js": "^3.5.2",
23
- "vue": "^3.5.12",
20
+ "@types/vue": "^2.0.0",
24
21
  "@typescript-eslint/parser": "^8.13.0",
25
22
  "@vitejs/plugin-vue": "^5.1.4",
23
+ "ant-design-vue": "4.x",
24
+ "axios": "^1.7.7",
26
25
  "eslint": "^9.14.0",
27
26
  "husky": "^9",
28
27
  "lint-staged": "^13.2.0",
29
28
  "typescript": "~5.5.4",
30
29
  "vite": "^5.4.9",
30
+ "vite-plugin-css-injected-by-js": "^3.5.2",
31
+ "vue": "^3.5.12",
31
32
  "vue-tsc": "^2.1.8"
32
33
  }
33
34
  }
package/src/App.vue CHANGED
@@ -8,18 +8,20 @@ type LocaleType = 'zh-CN' | 'en-US' | 'id-ID' | 'fil-PH';
8
8
  const color = ref<string>('#1677ff');
9
9
  const locale = ref<LocaleType>('zh-CN');
10
10
 
11
- const permissions = reactive(['CRANE.ROLE.QUERY',
12
- 'CRANE.ROLE.CREATE',
13
- 'CRANE.ROLE.UPDATE',
14
- 'CRANE.ROLE.DELETE',
15
- 'CRANE.ROLE.PERMISSION_ASSIGN',
16
- 'CRANE.ROLE.EMPLOYEE_ASSIGN',
17
- 'CRANE.ROLE.DETAIL_QUERY',
18
- 'CRANE.ROLE.EXPORT']);
11
+ const permissions = reactive([
12
+ 'CRANE.ROLE.QUERY',
13
+ 'CRANE.ROLE.CREATE',
14
+ 'CRANE.ROLE.UPDATE',
15
+ 'CRANE.ROLE.DELETE',
16
+ 'CRANE.ROLE.PERMISSION_ASSIGN',
17
+ 'CRANE.ROLE.EMPLOYEE_ASSIGN',
18
+ 'CRANE.ROLE.DETAIL_QUERY',
19
+ 'CRANE.ROLE.EXPORT'
20
+ ]);
19
21
  // const permissions = reactive(['CRANE.BUSINESS.QUERY', 'RANE.BUSINESS.CREATE', 'CRANE.BUSINESS.UPDATE', 'CRANE.BUSINESS.DELETE']);
20
22
  const changeColor = () => {
21
23
  color.value = color.value === '#f00' ? '#1677ff' : '#f00';
22
- }
24
+ }
23
25
  const changeLocale = () => {
24
26
  locale.value = locale.value === 'zh-CN' ? 'en-US' : 'zh-CN';
25
27
  }
@@ -36,44 +38,40 @@ const changeLocale = () => {
36
38
  <!-- 03541 -->
37
39
  <!-- 02124 -->
38
40
  <!-- 05184 -->
39
- <yqg-permission
41
+ <yqg-permission
40
42
  :permissions="permissions"
41
43
  workNumber="05184"
42
- businessCode="CRANE"
43
44
  :color="color"
44
45
  :locale="locale"
45
46
  @onSuccess="() => {console.log('成功')}"
46
47
  >
47
48
  </yqg-permission>
48
49
  </div>
49
- <!-- <data class="case-card">
50
+ <data class="case-card">
50
51
  <div>2:文字组件</div>
51
- <yqg-permission
52
- :permissions="permissions"
53
- businessCode="CRANE"
52
+ <yqg-permission
53
+ :permissions="[]"
54
54
  :color="color"
55
55
  workNumber="02124"
56
- type="text"
56
+ type="icon"
57
57
  >
58
58
  </yqg-permission>
59
59
  </data>
60
- -->
61
60
 
62
-
61
+
63
62
  <!-- <div class="case-card">
64
63
  <div>3:自定义</div>
65
- <yqg-permission
64
+ <yqg-permission
66
65
  :permissions="permissions"
67
- businessCode="CRANE"
68
66
  :color="color"
69
67
  workNumber="02124"
70
- locale="zh-CN"
68
+ locale="zh-CN"
71
69
  type="custom"
72
70
  >
73
71
  <div style="color: red;" slot="custom">自定义按钮</div>
74
72
  </yqg-permission>
75
73
  </div> -->
76
-
74
+
77
75
  </div>
78
76
 
79
77
  </template>
Binary file
@@ -18,7 +18,11 @@
18
18
  :label="t('applyPermission')"
19
19
  name="roleIds"
20
20
  :rules="[{ required: true, message: t('selectPlaceholder')}]">
21
+ <span v-if="!permissionList.length">
22
+ {{t('noPermissionTips')}}
23
+ </span>
21
24
  <CheckboxGroup
25
+ v-else
22
26
  v-model:value="formState.roleIds"
23
27
  style="display: block;"
24
28
  @change="onChangeHandler"
@@ -48,14 +52,6 @@
48
52
  :placeholder="t('applyReasonPlaceholder')"
49
53
  :auto-size="{ minRows: 2, maxRows: 5 }">
50
54
  </Textarea>
51
- <div style="margin-top: 4px;">
52
- <Tag
53
- :bordered="false"
54
- style="cursor: pointer;"
55
- v-for="item in reasons"
56
- @click="addReasonHandler(item)"
57
- :key="item">{{ item }}</Tag>
58
- </div>
59
55
  </FormItem>
60
56
 
61
57
  <FormItem :label="t('approvalProcess')">
@@ -82,7 +78,6 @@
82
78
  FormItem,
83
79
  CheckboxGroup,
84
80
  Textarea,
85
- Tag,
86
81
  message
87
82
  } from 'ant-design-vue';
88
83
  import SuccessModal from './success-modal.vue';
@@ -92,12 +87,6 @@
92
87
  import('./checkbox-item.vue')
93
88
  );
94
89
 
95
- const reasons = [
96
- t('applyReason1'),
97
- t('applyReason2'),
98
- t('applyReason3'),
99
- ]
100
-
101
90
  const props = defineProps({
102
91
  permissionList: {
103
92
  type: Array as () => PermissionType[],
@@ -132,18 +121,6 @@
132
121
  applyReason: '',
133
122
  submitWorkNumber: submitWorkNumber.value,
134
123
  });
135
-
136
- const addReasonHandler = (item: string) => {
137
- if (formState.applyReason.includes(item)) {
138
- return;
139
- }
140
- if (!formState.applyReason || formState.applyReason.endsWith('、')) {
141
- formState.applyReason += item;
142
- } else {
143
- formState.applyReason += `、${item}`;
144
- }
145
- formRef.value.validateFields(['applyReason']);
146
- }
147
124
 
148
125
  const handleOk = async() => {
149
126
  if (formState.roleIds.length > 5) {
@@ -1,18 +1,18 @@
1
1
  <template>
2
2
  <div class="crane-checkbox-line">
3
- <Checkbox
3
+ <Checkbox
4
4
  :value="item.roleId"
5
5
  :disabled="['OWNER', 'PENDING', 'NO'].includes(props.item.businessApplyType)"
6
6
  @change="onCheck">
7
7
  <div class="crane-flex-center crane-checkbox-label">
8
8
  <Tag
9
- v-if="item.securityLevel"
10
- :bordered="false"
11
- :color="levelMap[item.securityLevel].color"
9
+ v-if="item.securityLevel"
10
+ :bordered="false"
11
+ :style="{color: levelMap[item.securityLevel].color, background: levelMap[item.securityLevel].background}"
12
12
  class="crane-tag-position">
13
13
  {{ levelMap[item.securityLevel].text }}
14
14
  </Tag>
15
-
15
+
16
16
  <span>{{t(`operationType.${item.operationType}`)}}|
17
17
  </span>
18
18
  <Popover>
@@ -22,8 +22,8 @@
22
22
  <span class="crane-text-overflow">{{ item.name }}</span>
23
23
  </Popover>
24
24
  <Tag
25
- v-if="item.businessApplyType && item.businessApplyType !== 'TEMP_OWNER'"
26
- :bordered="false"
25
+ v-if="item.businessApplyType && item.businessApplyType !== 'TEMP_OWNER'"
26
+ :bordered="false"
27
27
  class="crane-tag-position crane-margin-left-4 crane-margin-right-0"
28
28
  :class="['PENDING', 'TEMP_OWNER'].includes(item.businessApplyType) ? '' : 'crane-disabled-color'">
29
29
  {{ statusMap[item.businessApplyType] }}
@@ -34,14 +34,14 @@
34
34
  <span v-else>{{ t('taday') }}</span>
35
35
  </template>
36
36
  <Tag
37
- v-if="item.businessApplyType === 'TEMP_OWNER'"
38
- :bordered="false"
37
+ v-if="item.businessApplyType === 'TEMP_OWNER'"
38
+ :bordered="false"
39
39
  class="crane-tag-position crane-margin-left-4 crane-margin-right-0"
40
40
  :class="['PENDING'].includes(item.businessApplyType) ? '' : 'crane-disabled-color'">
41
41
  {{ statusMap[item.businessApplyType] }}
42
42
  </Tag>
43
43
  </Popover>
44
-
44
+
45
45
  </div>
46
46
  </Checkbox>
47
47
 
@@ -88,16 +88,19 @@
88
88
 
89
89
  const levelMap:LevelMapType = {
90
90
  L1: {
91
- color: 'green',
92
- text: t('levels.L1')
91
+ color: '#1AA83B',
92
+ text: t('levels.L1'),
93
+ background: '#E3F9E9',
93
94
  },
94
95
  L2: {
95
- color: 'orange',
96
- text: t('levels.L2')
97
- },
96
+ color: '#F37D1C',
97
+ text: t('levels.L2'),
98
+ background: '#FFE4BA'
99
+ },
98
100
  L3: {
99
- color: 'red',
100
- text: t('levels.L3')
101
+ color: '#F2494B',
102
+ text: t('levels.L3'),
103
+ background: '#FDCDC5'
101
104
  },
102
105
  };
103
106
 
@@ -155,7 +158,7 @@
155
158
  }
156
159
  .crane-checkbox-line {
157
160
  margin-bottom: 12px;
158
- display: flex;
161
+ display: flex;
159
162
  align-items: center;
160
163
  }
161
164
  .crane-checkbox-line:last-child {
@@ -166,8 +169,8 @@
166
169
  }
167
170
  .crane-tag-position {
168
171
  margin-right: 4px;
169
- font-size: 10px;
170
- padding: 2px 4px;
172
+ font-size: 10px;
173
+ padding: 2px 4px;
171
174
  line-height: 12px;
172
175
  font-weight: 500;
173
176
  }
@@ -194,5 +197,5 @@
194
197
  overflow: hidden;
195
198
  text-overflow: ellipsis
196
199
  }
197
-
200
+
198
201
  </style>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <contextHolder></contextHolder>
2
+ <contextHolder></contextHolder>
3
3
  </template>
4
4
  <script lang="ts" setup>
5
5
  import { createVNode } from 'vue';
@@ -1,24 +1,36 @@
1
1
  <template>
2
2
  <ConfigProvider
3
- v-if="allPermissions.length"
4
- prefixCls="yqg-permission"
5
- :theme="{
6
- token: {
7
- colorPrimary: props.color,
8
- }}">
3
+ prefixCls="yqg-permission"
4
+ :theme="{
5
+ token: {
6
+ colorPrimary: props.color,
7
+ }
8
+ }"
9
+ >
9
10
  <div class="crane-wraper">
10
- <template v-if="type==='text'">
11
- <TypographyLink @click="showModal">{{t('permissionApply')}}</TypographyLink>
11
+ <template v-if="type === comType.ICON">
12
+ <FloatButton
13
+ ref="dragElement"
14
+ type="primary"
15
+ :tooltip="t('clickToApply')"
16
+ :style="{
17
+ right,
18
+ top: currentTop,
19
+ }"
20
+ >
21
+ <template #icon>
22
+ <img :src="applyIconUrl" />
23
+ </template>
24
+ </FloatButton>
12
25
  </template>
13
- <template v-else-if="type==='custom'">
26
+ <template v-else-if="type===comType.CUSTOM">
14
27
  <div @click="showModal">
15
- <slot name="custom"></slot>
28
+ <slot name="custom"/>
16
29
  </div>
17
30
  </template>
18
31
  <template v-else>
19
-
20
32
  <!-- 可申请 -->
21
- <template v-if="curStatus.status === statusMap.DEFAULT">
33
+ <template v-if="curStatus.status === STATUS_MAP.DEFAULT">
22
34
  <img
23
35
  :src="curStatus.imageUrl"
24
36
  height="200"
@@ -32,7 +44,7 @@
32
44
  </div>
33
45
  </template>
34
46
  <!-- 审批中 -->
35
- <template v-if="curStatus.status === statusMap.PENDING">
47
+ <template v-if="curStatus.status === STATUS_MAP.PENDING">
36
48
  <img
37
49
  :src="curStatus.imageUrl"
38
50
  height="200"
@@ -52,7 +64,7 @@
52
64
  </div>
53
65
  </template>
54
66
  <!-- 不可申请 -->
55
- <div v-if="curStatus.status === statusMap.NO" class="crane-wraper">
67
+ <div v-if="curStatus.status === STATUS_MAP.NO" class="crane-wraper">
56
68
  <img
57
69
  :src="curStatus.imageUrl"
58
70
  height="200"
@@ -83,20 +95,33 @@
83
95
  </ConfigProvider>
84
96
  </template>
85
97
  <script lang="ts" setup>
86
- import { ref, defineAsyncComponent, computed, watchEffect, watch} from 'vue';
87
- import {Button, ConfigProvider, TypographyLink, Popover } from 'ant-design-vue';
98
+ import { ref, defineAsyncComponent, computed, watchEffect, watch } from 'vue';
99
+ import { Button, ConfigProvider, Popover , message, FloatButton} from 'ant-design-vue';
88
100
  import applyUrl from '@/assets/applying.png';
101
+ import applyIconUrl from '@/assets/apply.png';
89
102
  import noauthority from '@/assets/noauthority.png';
90
103
  import Http from '../axios/index';
91
104
  import t from '../utils';
92
- const ApplyModal = defineAsyncComponent(() =>
93
- import('./apply-modal.vue')
94
- );
95
- const statusMap = {
105
+ import useDraggable from '../hooks/useDragable';
106
+
107
+ const STATUS_MAP = {
96
108
  DEFAULT: 'DEFAULT',
97
109
  PENDING: 'PENDING',
98
110
  NO: 'NO',
99
- }
111
+ } as const;
112
+
113
+ const comType = {
114
+ ICON: 'icon',
115
+ DEFAULT: 'default',
116
+ CUSTOM: 'custom',
117
+ };
118
+
119
+ // 重置 message 类名,避免被全局样式覆盖
120
+ message.config({prefixCls: 'yqg-permission-message'});
121
+
122
+ const ApplyModal = defineAsyncComponent(() =>import('./apply-modal.vue'));
123
+
124
+ const emit = defineEmits(['onSuccess']);
100
125
 
101
126
  const props = defineProps({
102
127
  workNumber: {
@@ -107,10 +132,6 @@
107
132
  type: [String, Array<String>],
108
133
  default: []
109
134
  },
110
- businessCode: {
111
- type: String,
112
- default: ''
113
- },
114
135
  locale: {
115
136
  type: String,
116
137
  default: 'zh-CN'
@@ -123,28 +144,49 @@
123
144
  type: String,
124
145
  default: 'default'
125
146
  },
147
+ right: {
148
+ type: String,
149
+ default: '100px'
150
+ },
151
+ top: {
152
+ type: String,
153
+ default: '100px'
154
+ }
126
155
  });
127
- const emit = defineEmits(['onSuccess']);
156
+
128
157
 
129
158
  const open = ref(false);
130
159
  const curApproving = ref<PermissionType>();
131
160
  let permissionList = ref<PermissionListType>([]);
161
+
162
+
163
+
132
164
  let curStatus = ref<Record<string, any>>({
133
165
  imageUrl: noauthority,
134
166
  status: '',
135
167
  })
168
+
136
169
  const allPermissions = computed(() => {
137
170
  if (Array.isArray(props.permissions)) {
138
171
  return props.permissions;
139
- } else {
140
- return props.permissions.split(',');
141
172
  }
173
+
174
+ return props.permissions.split(',');
142
175
  });
143
176
 
177
+ const businessCode = computed(() => {
178
+ const code = allPermissions.value[0] || '';
179
+ if (!code) return '';
180
+
181
+ return code.split('.')[0];
182
+ })
183
+
144
184
  const formatPermissionsData = (data: PermissionListType) => {
145
185
  const arr:PermissionListType = [];
146
186
  const flattenData = (list: PermissionListType) => {
147
187
  list.forEach((item) => {
188
+ // 去掉名字中第一个.前面的字符串,会有多个.
189
+ item.name = item.name.replace(/^[^.]+\./, '');
148
190
  if (item.children) {
149
191
  flattenData(item.children);
150
192
  } else {
@@ -153,13 +195,19 @@
153
195
  })
154
196
  };
155
197
  flattenData(data);
198
+
156
199
  // 需要排序,规则:businessApplyType 为 null 在前面, PENDING. OWNER 在中间, NO 在后面
157
200
  // 然后再根据 L1, L2, L3 排序
158
201
  const sort = [ null, 'TEMP_OWNER', 'PENDING', 'OWNER', 'NO'];
159
202
  const levelSort = ['L1', 'L2', 'L3'];
203
+ const sortMap = new Map(sort.map((value, index) => [value, index]));
204
+ const levelSortMap = new Map(levelSort.map((value, index) => [value, index]));
205
+
160
206
  arr.sort((a, b) => {
161
- return sort.indexOf(a.businessApplyType) - sort.indexOf(b.businessApplyType) || levelSort.indexOf(a.securityLevel) - levelSort.indexOf(b.securityLevel);
207
+ return (sortMap.get(a.businessApplyType) ?? 0) - (sortMap.get(b.businessApplyType) ?? 0)
208
+ || (levelSortMap.get(a.securityLevel) ?? 0) - (levelSortMap.get(b.securityLevel) ?? 0);
162
209
  });
210
+
163
211
  return arr;
164
212
  }
165
213
 
@@ -167,16 +215,18 @@
167
215
  if (!data.length) {
168
216
  return {
169
217
  imageUrl: noauthority,
170
- status: '',
218
+ status: ''
171
219
  };
172
220
  }
173
- const current = data.find((per) => per.businessApplyType === statusMap.PENDING);
174
- const cannotApply = data.every((per) => per.businessApplyType === statusMap.NO);
221
+
222
+ const current = data.find((per) => per.businessApplyType === STATUS_MAP.PENDING);
223
+ const cannotApply = data.every((per) => per.businessApplyType === STATUS_MAP.NO);
224
+
175
225
  if (current) {
176
226
  curApproving.value = current;
177
227
  return {
178
228
  imageUrl: applyUrl,
179
- status: statusMap.PENDING,
229
+ status: STATUS_MAP.PENDING,
180
230
  tips: t('status.PENDING'),
181
231
  url: current.oaFlowUrl,
182
232
  };
@@ -185,34 +235,38 @@
185
235
  if (cannotApply) {
186
236
  return {
187
237
  imageUrl: noauthority,
188
- status: statusMap.NO,
238
+ status: STATUS_MAP.NO,
189
239
  tips: data[0].admin?.map((item) => `${item.name}(${item.departmentName})`)?.join('、'),
190
240
  };
191
241
  };
242
+
192
243
  return {
193
244
  imageUrl: noauthority,
194
- status: statusMap.DEFAULT,
245
+ status: STATUS_MAP.DEFAULT,
195
246
  }
196
247
  };
197
248
 
198
249
  const goViewApproval = () => {
199
- console.log(curApproving.value)
200
- if (curApproving.value?.oaFlowUrl) {
201
- window.open(curApproving.value.oaFlowUrl);
202
- }
250
+ const url = curApproving.value?.oaFlowUrl;
251
+ if (!url) return;
252
+
253
+ window.open(url, '_blank');
203
254
  };
204
255
 
205
256
  const getPermissions = async () => {
206
- if (!allPermissions.value.length || !props.businessCode || !props.workNumber) return;
257
+ const { workNumber } = props;
258
+ const permissions = allPermissions.value;
259
+
260
+ if (!permissions?.length || !workNumber) return;
207
261
 
208
262
  const params = {
209
- businessCode: props.businessCode,
210
- features: allPermissions.value.toString(),
211
- workNumber: props.workNumber,
263
+ workNumber,
264
+ features: permissions.toString(),
212
265
  };
213
266
 
214
- let res = await Http.getPermissions(params);
267
+ const res = await Http.getPermissions(params);
215
268
  permissionList.value = formatPermissionsData(res.body || []);
269
+
216
270
  curStatus.value = getStatus(permissionList.value);
217
271
  };
218
272
 
@@ -221,14 +275,16 @@
221
275
  open.value = !open.value;
222
276
  };
223
277
 
278
+ const { currentTop, dragElement } = useDraggable(props, showModal);
279
+
224
280
  watchEffect(getPermissions);
225
281
 
226
282
  watch(() => props.locale, (cur, pre) => {
227
283
  if (cur === pre) return;
228
284
  localStorage.setItem('permission_locale', props.locale);
229
285
  }, {immediate: true})
230
-
231
286
  </script>
287
+
232
288
  <style scoped>
233
289
  .crane-wraper {
234
290
  display: flex;
@@ -245,3 +301,4 @@
245
301
  }
246
302
  </style>
247
303
 
304
+ ../hooks/useDragable