@coopenomics/desktop 2.2.2 → 2.2.4
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/CHANGELOG.md +16 -0
- package/package.json +5 -5
- package/src/desktops/User/model/index.ts +11 -10
- package/src/entities/Desktop/model/types.ts +1 -0
- package/src/env.d.ts +1 -0
- package/src/features/User/LoginUser/model/index.ts +17 -3
- package/src/pages/Registrator/SignUp/GenerateAccount.vue +3 -1
- package/src/pages/Registrator/SignUp/SignStatement.vue +135 -171
- package/src/widgets/Desktop/SecondLevelMenuList/SecondLevelMenuList.vue +33 -3
- package/src/widgets/Header/SettingsDropdown/SettingsDropdown.vue +1 -1
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,22 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
+
## [2.2.4](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.4) (2025-01-17)
|
7
|
+
|
8
|
+
**Note:** Version bump only for package @coopenomics/desktop
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
## [2.2.3](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.3) (2025-01-16)
|
15
|
+
|
16
|
+
**Note:** Version bump only for package @coopenomics/desktop
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
6
22
|
## [2.2.1](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.1) (2025-01-14)
|
7
23
|
|
8
24
|
**Note:** Version bump only for package @coopenomics/desktop
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@coopenomics/desktop",
|
3
|
-
"version": "2.2.
|
3
|
+
"version": "2.2.4",
|
4
4
|
"description": "A Desktop Project",
|
5
5
|
"productName": "Desktop App",
|
6
6
|
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
@@ -20,8 +20,8 @@
|
|
20
20
|
"prepublishOnly": "npm run build:lib"
|
21
21
|
},
|
22
22
|
"dependencies": {
|
23
|
-
"@coopenomics/controller": "2.2.
|
24
|
-
"@coopenomics/sdk": "2.2.
|
23
|
+
"@coopenomics/controller": "2.2.4",
|
24
|
+
"@coopenomics/sdk": "2.2.4",
|
25
25
|
"@dicebear/collection": "^9.0.1",
|
26
26
|
"@dicebear/core": "^9.0.1",
|
27
27
|
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
@@ -45,7 +45,7 @@
|
|
45
45
|
"@wharfkit/wallet-plugin-privatekey": "^1.1.0",
|
46
46
|
"axios": "^1.2.1",
|
47
47
|
"compression": "^1.7.4",
|
48
|
-
"cooptypes": "
|
48
|
+
"cooptypes": "2.2.4",
|
49
49
|
"dompurify": "^3.1.7",
|
50
50
|
"dotenv": "^16.4.5",
|
51
51
|
"email-regex": "^5.0.0",
|
@@ -94,5 +94,5 @@
|
|
94
94
|
"npm": ">= 6.13.4",
|
95
95
|
"yarn": ">= 1.21.1"
|
96
96
|
},
|
97
|
-
"gitHead": "
|
97
|
+
"gitHead": "cbfe37dbba00574ec4f1ca354ecf5a4438d72bc1"
|
98
98
|
}
|
@@ -24,6 +24,17 @@ export const manifest = {
|
|
24
24
|
path: '/:coopname/user',
|
25
25
|
name: 'home',
|
26
26
|
children: [
|
27
|
+
{
|
28
|
+
meta: {
|
29
|
+
title: 'Подключение',
|
30
|
+
icon: 'fas fa-link',
|
31
|
+
roles: ['user'],
|
32
|
+
conditions: 'isCoop === true && coopname === "voskhod"',
|
33
|
+
},
|
34
|
+
path: '/:coopname/connect',
|
35
|
+
name: 'connect',
|
36
|
+
component: markRaw(ConnectionPage),
|
37
|
+
},
|
27
38
|
{
|
28
39
|
meta: {
|
29
40
|
title: 'Карта пайщика',
|
@@ -141,17 +152,7 @@ export const manifest = {
|
|
141
152
|
component: markRaw(UnionPageListOfCooperatives),
|
142
153
|
|
143
154
|
},
|
144
|
-
{
|
145
|
-
// meta: {
|
146
|
-
// title: 'Подключения',
|
147
|
-
// icon: 'fas fa-link',
|
148
|
-
// roles: ['chairman', 'member'],
|
149
|
-
// },
|
150
|
-
path: '/:coopname/connect',
|
151
|
-
name: 'connect',
|
152
|
-
component: markRaw(ConnectionPage),
|
153
155
|
|
154
|
-
},
|
155
156
|
{
|
156
157
|
path: '/:coopname/contacts',
|
157
158
|
name: 'contacts',
|
package/src/env.d.ts
CHANGED
@@ -5,6 +5,7 @@ import {client} from 'src/shared/api/client'
|
|
5
5
|
import { useCurrentUserStore } from 'src/entities/User'
|
6
6
|
import { useSystemStore } from 'src/entities/System/model'
|
7
7
|
import { useRegistratorStore } from 'src/entities/Registrator'
|
8
|
+
import type { ITokens } from 'src/shared/lib/types/user'
|
8
9
|
|
9
10
|
export function useLoginUser() {
|
10
11
|
const globalStore = useGlobalStore()
|
@@ -12,10 +13,23 @@ export function useLoginUser() {
|
|
12
13
|
const system = useSystemStore()
|
13
14
|
|
14
15
|
async function login(email: string, wif: string): Promise<void> {
|
15
|
-
const auth = await api.loginUser(email, wif)
|
16
|
+
const auth = await api.loginUser(email, wif);
|
17
|
+
const { tokens, account } = await client.login(email, wif);
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
+
// Создаём объект tokens с правильными типами
|
20
|
+
const adaptedTokens: ITokens = {
|
21
|
+
access: {
|
22
|
+
token: tokens.access.token,
|
23
|
+
expires: new Date(tokens.access.expires as string),
|
24
|
+
},
|
25
|
+
refresh: {
|
26
|
+
token: tokens.refresh.token,
|
27
|
+
expires: new Date(tokens.refresh.expires as string),
|
28
|
+
},
|
29
|
+
};
|
30
|
+
|
31
|
+
await globalStore.setWif(account.username, wif);
|
32
|
+
await globalStore.setTokens(adaptedTokens);
|
19
33
|
|
20
34
|
const session = useSessionStore()
|
21
35
|
await session.init()
|
@@ -34,6 +34,8 @@ import { ref, computed } from 'vue'
|
|
34
34
|
import { useCreateUser } from 'src/features/User/CreateUser'
|
35
35
|
import { Notify, copyToClipboard } from 'quasar'
|
36
36
|
import { useRegistratorStore } from 'src/entities/Registrator'
|
37
|
+
import { Classes } from '@coopenomics/sdk'
|
38
|
+
|
37
39
|
const store = useRegistratorStore()
|
38
40
|
|
39
41
|
import { FailAlert } from 'src/shared/api'
|
@@ -43,7 +45,7 @@ const i_save = ref(false)
|
|
43
45
|
const account = ref(store.state.account)
|
44
46
|
|
45
47
|
if (!account.value.private_key || !account.value.public_key || !account.value.username)
|
46
|
-
account.value =
|
48
|
+
account.value = new Classes.Account()
|
47
49
|
|
48
50
|
const email = computed(() => store.state.email)
|
49
51
|
const userData = computed(() => store.state.userData)
|
@@ -1,189 +1,153 @@
|
|
1
|
-
<template lang=
|
2
|
-
div
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
<template lang="pug">
|
2
|
+
div
|
3
|
+
q-step(
|
4
|
+
:name="store.steps.SignStatement"
|
5
|
+
title="Подпишите заявление на вступление"
|
6
|
+
:done="store.isStepDone('SignStatement')"
|
7
|
+
)
|
8
|
+
div(v-if="onSign")
|
9
|
+
Loader(:text="loadingText")
|
10
|
+
|
11
|
+
div(v-else)
|
12
|
+
// Контейнер, внутри которого класс Canvas создаёт <canvas>
|
13
|
+
.bg-grey-2(
|
14
|
+
v-if="!onSign"
|
15
|
+
ref="around"
|
16
|
+
style="border: 0.1px solid grey; min-height: 300px;"
|
16
17
|
)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
<script lang="ts" setup>
|
28
|
-
import { ref,
|
29
|
-
import {
|
30
|
-
import {
|
31
|
-
import {
|
32
|
-
import {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
const
|
40
|
-
|
41
|
-
const
|
42
|
-
|
43
|
-
const
|
44
|
-
|
45
|
-
|
46
|
-
let
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
18
|
+
p.text-center.full-width Оставьте собственноручную подпись в рамке
|
19
|
+
.q-mt-lg.q-mb-lg
|
20
|
+
q-btn.col-md-4.col-xs-12(flat @click="store.prev()")
|
21
|
+
i.fa.fa-arrow-left
|
22
|
+
span.q-ml-md назад
|
23
|
+
q-btn.col-md-4.col-xs-12(flat @click="clearCanvas")
|
24
|
+
span.q-ml-md очистить
|
25
|
+
q-btn.col-md-4.col-xs-12(color="primary" label="Продолжить" @click="setSignature")
|
26
|
+
</template>
|
27
|
+
|
28
|
+
<script lang="ts" setup>
|
29
|
+
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
30
|
+
import { Notify } from 'quasar'
|
31
|
+
import { FailAlert } from 'src/shared/api'
|
32
|
+
import { Loader } from 'src/shared/ui/Loader'
|
33
|
+
import { useRegistratorStore } from 'src/entities/Registrator'
|
34
|
+
import { useCreateUser } from 'src/features/User/CreateUser'
|
35
|
+
|
36
|
+
// Импортируем класс
|
37
|
+
import { Classes } from '@coopenomics/sdk'
|
38
|
+
|
39
|
+
const store = useRegistratorStore()
|
40
|
+
const createUser = useCreateUser()
|
41
|
+
|
42
|
+
const around = ref<HTMLElement | null>(null)
|
43
|
+
const onSign = ref(false)
|
44
|
+
const loadingText = ref('')
|
45
|
+
|
46
|
+
// Экземпляр Canvas
|
47
|
+
let canvasClass: Classes.Canvas | null = null
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Инициализация Canvas с задержкой
|
51
|
+
*/
|
52
|
+
const initCanvas = () => {
|
53
|
+
|
54
|
+
// Ждём 200ms, чтобы Quasar завершил рендеринг
|
55
|
+
setTimeout(() => {
|
56
|
+
if (around.value)
|
57
|
+
canvasClass = new Classes.Canvas(around.value, {
|
58
|
+
lineWidth: 5,
|
59
|
+
strokeStyle: '#000',
|
60
|
+
})
|
61
|
+
}, 200)
|
59
62
|
}
|
60
63
|
|
64
|
+
/**
|
65
|
+
* Следим за текущим шагом
|
66
|
+
*/
|
67
|
+
watch(
|
68
|
+
() => store.state.step,
|
69
|
+
(newStep) => {
|
70
|
+
if (newStep === store.steps.SignStatement) {
|
71
|
+
nextTick(() => initCanvas())
|
72
|
+
}
|
73
|
+
}
|
74
|
+
)
|
75
|
+
|
76
|
+
/**
|
77
|
+
* Если при загрузке уже SignStatement — инициализируем
|
78
|
+
*/
|
79
|
+
onMounted(() => {
|
80
|
+
if (store.state.step === store.steps.SignStatement) {
|
81
|
+
nextTick(() => initCanvas())
|
82
|
+
}
|
83
|
+
})
|
61
84
|
|
62
|
-
|
63
|
-
|
64
|
-
|
85
|
+
/**
|
86
|
+
* При размонтировании снимаем слушатели
|
87
|
+
*/
|
88
|
+
onBeforeUnmount(() => {
|
89
|
+
canvasClass?.destroy()
|
90
|
+
})
|
65
91
|
|
66
|
-
|
67
|
-
|
68
|
-
|
92
|
+
/**
|
93
|
+
* Очистка холста
|
94
|
+
*/
|
95
|
+
const clearCanvas = () => {
|
96
|
+
canvasClass?.clearCanvas()
|
69
97
|
}
|
70
98
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
loadingText.value = 'Подписываем соглашение о порядке и правилах использования ЭЦП'
|
84
|
-
|
85
|
-
await createUser.signSignatureAgreement()
|
86
|
-
|
87
|
-
loadingText.value = 'Подписываем пользовательское соглашение'
|
88
|
-
|
89
|
-
await createUser.signUserAgreement()
|
90
|
-
|
91
|
-
loadingText.value = 'Подписываем заявление'
|
92
|
-
|
93
|
-
await createUser.signStatement()
|
94
|
-
|
95
|
-
//посылаем
|
96
|
-
await createUser.sendStatementAndAgreements()
|
97
|
-
loadingText.value = ''
|
98
|
-
|
99
|
-
onSign.value = false
|
100
|
-
store.next()
|
101
|
-
} catch (e: any) {
|
102
|
-
onSign.value = false
|
103
|
-
FailAlert(e.message)
|
104
|
-
}
|
99
|
+
/**
|
100
|
+
* Получение подписи + проверка пустоты
|
101
|
+
*/
|
102
|
+
const setSignature = async () => {
|
103
|
+
if (!canvasClass) {
|
104
|
+
Notify.create({
|
105
|
+
message: 'Пожалуйста, оставьте собственноручную подпись в окне',
|
106
|
+
color: 'negative',
|
107
|
+
})
|
108
|
+
return
|
109
|
+
}
|
105
110
|
|
111
|
+
const sign = canvasClass.getSignature()
|
112
|
+
// Проверим, есть ли хоть один ненулевой пиксель
|
113
|
+
const ctx = canvasClass.ctx
|
114
|
+
const { width, height } = canvasClass.canvas
|
115
|
+
const data = ctx.getImageData(0, 0, width, height).data
|
116
|
+
const isEmpty = !data.some((channel) => channel !== 0)
|
106
117
|
|
107
|
-
|
118
|
+
if (!sign || isEmpty) {
|
119
|
+
FailAlert('Пожалуйста, оставьте собственноручную подпись в окне')
|
120
|
+
return
|
121
|
+
}
|
108
122
|
|
109
|
-
|
110
|
-
|
111
|
-
|
123
|
+
try {
|
124
|
+
onSign.value = true
|
125
|
+
store.state.signature = sign
|
112
126
|
|
113
|
-
|
114
|
-
|
115
|
-
init()
|
116
|
-
}
|
117
|
-
})
|
127
|
+
loadingText.value = 'Подписываем положение о ЦПП "Цифровой Кошелёк"'
|
128
|
+
await createUser.signWalletAgreement()
|
118
129
|
|
119
|
-
|
120
|
-
|
121
|
-
})
|
130
|
+
loadingText.value = 'Подписываем соглашение о политике конфиденциальности'
|
131
|
+
await createUser.signPrivacyAgreement()
|
122
132
|
|
123
|
-
|
124
|
-
|
125
|
-
})
|
133
|
+
loadingText.value = 'Подписываем соглашение о порядке и правилах использования ЭЦП'
|
134
|
+
await createUser.signSignatureAgreement()
|
126
135
|
|
127
|
-
|
128
|
-
|
129
|
-
})
|
136
|
+
loadingText.value = 'Подписываем пользовательское соглашение'
|
137
|
+
await createUser.signUserAgreement()
|
130
138
|
|
131
|
-
|
132
|
-
|
133
|
-
windowWidth.value = around.value?.offsetWidth || 0
|
134
|
-
windowHeight.value = around.value?.offsetHeight || 0
|
139
|
+
loadingText.value = 'Подписываем заявление'
|
140
|
+
await createUser.signStatement()
|
135
141
|
|
136
|
-
|
142
|
+
// Отправка
|
143
|
+
await createUser.sendStatementAndAgreements()
|
137
144
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
context.lineWidth = 5
|
145
|
-
context.lineJoin = 'round'
|
146
|
-
context.lineCap = 'round'
|
147
|
-
context.strokeStyle = '#000'
|
145
|
+
loadingText.value = ''
|
146
|
+
onSign.value = false
|
147
|
+
store.next()
|
148
|
+
} catch (err: any) {
|
149
|
+
onSign.value = false
|
150
|
+
FailAlert(err.message)
|
148
151
|
}
|
149
|
-
}
|
150
|
-
|
151
|
-
|
152
|
-
const clearCanvas = (): void => {
|
153
|
-
context?.clearRect(0, 0, canvas.value?.width || 0, canvas.value?.height || 0)
|
154
|
-
}
|
155
|
-
|
156
|
-
const startDrawing = (e: MouseEvent | TouchEvent): void => {
|
157
|
-
e.preventDefault()
|
158
|
-
drawing.value = true
|
159
|
-
const rect = canvas.value?.getBoundingClientRect()
|
160
|
-
lastX =
|
161
|
-
e instanceof MouseEvent
|
162
|
-
? e.clientX - (rect?.left || 0)
|
163
|
-
: e.touches[0].clientX - (rect?.left || 0)
|
164
|
-
lastY =
|
165
|
-
e instanceof MouseEvent ? e.clientY - (rect?.top || 0) : e.touches[0].clientY - (rect?.top || 0)
|
166
|
-
}
|
167
|
-
|
168
|
-
const endDrawing = (): void => {
|
169
|
-
drawing.value = false
|
170
|
-
}
|
171
|
-
|
172
|
-
const draw = (e: MouseEvent | TouchEvent): void => {
|
173
|
-
e.preventDefault()
|
174
|
-
if (!drawing.value) return
|
175
|
-
context?.beginPath()
|
176
|
-
context?.moveTo(lastX, lastY)
|
177
|
-
const rect = canvas.value?.getBoundingClientRect()
|
178
|
-
const x =
|
179
|
-
e instanceof MouseEvent
|
180
|
-
? e.clientX - (rect?.left || 0)
|
181
|
-
: e.touches[0].clientX - (rect?.left || 0)
|
182
|
-
const y =
|
183
|
-
e instanceof MouseEvent ? e.clientY - (rect?.top || 0) : e.touches[0].clientY - (rect?.top || 0)
|
184
|
-
context?.lineTo(x, y)
|
185
|
-
context?.stroke()
|
186
|
-
lastX = x
|
187
|
-
lastY = y
|
188
|
-
}
|
189
|
-
</script>
|
152
|
+
}
|
153
|
+
</script>
|
@@ -25,7 +25,7 @@ q-list(
|
|
25
25
|
import { useRoute, useRouter } from 'vue-router';
|
26
26
|
import { useDesktopStore } from 'src/entities/Desktop/model';
|
27
27
|
import { type IRoute } from 'src/entities/Desktop/model/types';
|
28
|
-
import { COOPNAME } from 'src/shared/config';
|
28
|
+
import { COOPNAME } from 'src/shared/config';
|
29
29
|
|
30
30
|
const desktop = useDesktopStore()
|
31
31
|
const routes = ref<IRoute[]>([])
|
@@ -33,11 +33,41 @@ import { COOPNAME } from 'src/shared/config';
|
|
33
33
|
const router = useRouter()
|
34
34
|
const user = useCurrentUserStore()
|
35
35
|
|
36
|
+
const evaluateCondition = (condition: string, context: Record<string, any>): boolean => {
|
37
|
+
try {
|
38
|
+
const func = new Function(...Object.keys(context), `return ${condition};`);
|
39
|
+
return func(...Object.values(context));
|
40
|
+
} catch (error) {
|
41
|
+
console.error('Error evaluating condition:', error);
|
42
|
+
return false;
|
43
|
+
}
|
44
|
+
};
|
45
|
+
|
36
46
|
const init = () => {
|
47
|
+
const isCoop = user.userAccount?.type === 'organization' &&
|
48
|
+
user.userAccount?.private_data &&
|
49
|
+
'type' in user.userAccount.private_data &&
|
50
|
+
user.userAccount.private_data.type === 'coop';
|
51
|
+
|
37
52
|
const userRole = user.userAccount?.role || 'user';
|
53
|
+
|
54
|
+
const context = {
|
55
|
+
isCoop,
|
56
|
+
userRole,
|
57
|
+
userAccount: user.userAccount,
|
58
|
+
coopname: COOPNAME,
|
59
|
+
// любые другие свойства, которые нужно использовать в условиях
|
60
|
+
};
|
61
|
+
|
38
62
|
routes.value = (desktop.getSecondLevel(route) as unknown as IRoute[]).filter(
|
39
|
-
|
40
|
-
|
63
|
+
(route) => {
|
64
|
+
const rolesMatch = route.meta?.roles?.includes(userRole) || route.meta?.roles?.length === 0;
|
65
|
+
const conditionMatch = route.meta?.conditions
|
66
|
+
? evaluateCondition(route.meta.conditions, context)
|
67
|
+
: true; // Если условия нет, пропускаем проверку
|
68
|
+
return rolesMatch && conditionMatch;
|
69
|
+
}
|
70
|
+
);
|
41
71
|
}
|
42
72
|
|
43
73
|
// Функция проверки активного маршрута
|
@@ -9,7 +9,7 @@ q-btn-dropdown(flat :size="isMobile ? 'sm' : 'md'" :dense="isMobile" stretch ico
|
|
9
9
|
q-icon(name="fa-solid fa-wrench").q-mr-sm
|
10
10
|
span.font10px НАСТРОЙКИ ПАЙЩИКА
|
11
11
|
|
12
|
-
q-item(v-if="loggedIn && isChairman" flat clickable v-close-popup @click="open('members')")
|
12
|
+
q-item(v-if="loggedIn && (isChairman || isMember)" flat clickable v-close-popup @click="open('members')")
|
13
13
|
q-item-section
|
14
14
|
q-item-label
|
15
15
|
q-icon(name="fa-solid fa-hammer").q-mr-sm
|