@coopenomics/desktop 2.2.9 → 2.2.10
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 +11 -0
- package/package.json +5 -5
- package/src/boot/init-stores.ts +8 -2
- package/src/desktops/User/model/index.ts +11 -10
- package/src/entities/Account/api/index.ts +11 -2
- package/src/entities/Account/model/store.ts +15 -5
- package/src/entities/Account/types/index.ts +9 -1
- package/src/entities/Session/model/store.ts +2 -3
- package/src/features/Account/UpdateAccount/api/index.ts +14 -0
- package/src/features/Account/UpdateAccount/index.ts +1 -0
- package/src/features/Account/UpdateAccount/model/index.ts +16 -0
- package/src/features/Branch/SelectBranch/ui/SelectBranchOverlay.vue +3 -2
- package/src/features/User/AddUser/ui/AddUserDialog/AddUserDialog.vue +3 -0
- package/src/features/User/Logout/model/index.ts +3 -3
- package/src/pages/Cooperative/ListOfBranches/ui/ListOfBranchesPage.vue +2 -4
- package/src/shared/lib/composables/useEditableData.ts +1 -1
- package/src/shared/ui/EditableEntrepreneurCard/EditableEntrepreneurCard.vue +170 -0
- package/src/shared/ui/EditableEntrepreneurCard/index.ts +1 -0
- package/src/shared/ui/EditableIndividualCard/EditableIndividualCard.vue +203 -0
- package/src/shared/ui/EditableIndividualCard/index.ts +1 -0
- package/src/shared/ui/EditableOrganizationCard/EditableOrganizationCard.vue +215 -0
- package/src/shared/ui/EditableOrganizationCard/index.ts +1 -0
- package/src/shared/ui/UserDataForm/OrganizationDataForm/OrganizationDataForm.vue +1 -0
- package/src/widgets/Cooperative/Participants/ListOfParticipants/ui/ListOfParticipantsWidget.vue +154 -71
- package/src/widgets/IndividualCard/ui/IndividualCard.vue +23 -23
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,17 @@
|
|
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.10](https://github.com/coopenomics/mono/compare/v2.2.9...v2.2.10) (2025-03-27)
|
7
|
+
|
8
|
+
|
9
|
+
### Bug Fixes
|
10
|
+
|
11
|
+
* add Zeus type ([7bcb6e3](https://github.com/coopenomics/mono/commit/7bcb6e30a77b0ab89c5293188b58f08f19c8761e))
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
6
17
|
## [2.2.9](https://github.com/coopenomics/mono/compare/v2.2.8...v2.2.9) (2025-03-12)
|
7
18
|
|
8
19
|
**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.10",
|
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.10",
|
24
|
+
"@coopenomics/sdk": "2.2.10",
|
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": "2.2.
|
48
|
+
"cooptypes": "2.2.10",
|
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": "e5e28ff652112fe3bacb8036a898a1344ad361fa"
|
98
98
|
}
|
package/src/boot/init-stores.ts
CHANGED
@@ -28,8 +28,14 @@ export default boot(async ({ router }) => {
|
|
28
28
|
await cardStore.initWallet();
|
29
29
|
|
30
30
|
// Загрузка аккаунта
|
31
|
-
if (session.isAuth && session.username)
|
32
|
-
|
31
|
+
if (session.isAuth && session.username){
|
32
|
+
try {
|
33
|
+
await account.getAccount(session.username)
|
34
|
+
} catch(e){
|
35
|
+
session.close()
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
33
39
|
|
34
40
|
// Добавление динамических маршрутов как дочерних к 'base'
|
35
41
|
const baseRoute = router.getRoutes().find(route => route.name === 'base');
|
@@ -160,16 +160,6 @@ export const manifest = {
|
|
160
160
|
|
161
161
|
},
|
162
162
|
|
163
|
-
{
|
164
|
-
path: '/:coopname/contacts',
|
165
|
-
name: 'contacts',
|
166
|
-
component: markRaw(ContactsPage),
|
167
|
-
meta: {
|
168
|
-
title: 'Контакты',
|
169
|
-
icon: 'fa-solid fa-info',
|
170
|
-
roles: [],
|
171
|
-
},
|
172
|
-
},
|
173
163
|
// {
|
174
164
|
// path: '/:coopname/marketplace',
|
175
165
|
// name: 'marketplace',
|
@@ -251,6 +241,17 @@ export const manifest = {
|
|
251
241
|
// },
|
252
242
|
// ],
|
253
243
|
// },
|
244
|
+
{
|
245
|
+
path: '/:coopname/contacts',
|
246
|
+
name: 'contacts',
|
247
|
+
component: markRaw(ContactsPage),
|
248
|
+
meta: {
|
249
|
+
title: 'Контакты',
|
250
|
+
icon: 'fa-solid fa-info',
|
251
|
+
roles: [],
|
252
|
+
},
|
253
|
+
},
|
254
|
+
|
254
255
|
// {
|
255
256
|
// meta: {
|
256
257
|
// title: 'Поддержка',
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { client } from 'src/shared/api/client';
|
2
2
|
import { Queries } from '@coopenomics/sdk';
|
3
|
-
import type { IAccount } from '../types';
|
3
|
+
import type { IAccount, IAccounts, IGetAccounts } from '../types';
|
4
4
|
|
5
5
|
async function getAccount(username: string): Promise<IAccount | undefined> {
|
6
6
|
const { [Queries.Accounts.GetAccount.name]: output } = await client.Query(Queries.Accounts.GetAccount.query, {
|
@@ -12,6 +12,15 @@ async function getAccount(username: string): Promise<IAccount | undefined> {
|
|
12
12
|
return output;
|
13
13
|
}
|
14
14
|
|
15
|
+
async function getAccounts(variables?: IGetAccounts): Promise<IAccounts> {
|
16
|
+
const { [Queries.Accounts.GetAccounts.name]: output } = await client.Query(Queries.Accounts.GetAccounts.query, {
|
17
|
+
variables
|
18
|
+
});
|
19
|
+
|
20
|
+
return output;
|
21
|
+
}
|
22
|
+
|
15
23
|
export const api ={
|
16
|
-
getAccount
|
24
|
+
getAccount,
|
25
|
+
getAccounts
|
17
26
|
}
|
@@ -1,25 +1,35 @@
|
|
1
1
|
import { defineStore } from 'pinia'
|
2
2
|
import { ref, Ref } from 'vue'
|
3
3
|
import { api } from '../api'
|
4
|
-
import type { IAccount } from '../types';
|
4
|
+
import type { IAccount, IAccounts, IGetAccounts } from '../types';
|
5
5
|
|
6
6
|
const namespace = 'accountStore';
|
7
7
|
|
8
|
-
interface
|
8
|
+
interface IAccountStore {
|
9
9
|
account: Ref<IAccount | undefined>
|
10
|
+
accounts: Ref<IAccounts>
|
10
11
|
getAccount: (username: string) => Promise<IAccount | undefined>;
|
12
|
+
getAccounts: (data?: IGetAccounts) => Promise<IAccounts>;
|
11
13
|
}
|
12
14
|
|
13
|
-
export const useAccountStore = defineStore(namespace, ():
|
15
|
+
export const useAccountStore = defineStore(namespace, (): IAccountStore => {
|
14
16
|
const account = ref<IAccount>()
|
17
|
+
const accounts = ref<IAccounts>({items: [], totalCount: 0, totalPages: 0, currentPage: 1})
|
15
18
|
|
16
|
-
const getAccount = async (username: string) => {
|
19
|
+
const getAccount = async (username: string): Promise<IAccount | undefined> => {
|
17
20
|
account.value = await api.getAccount(username);
|
18
21
|
return account.value
|
19
22
|
};
|
23
|
+
|
24
|
+
const getAccounts = async(data?: IGetAccounts): Promise<IAccounts> => {
|
25
|
+
accounts.value = await api.getAccounts(data);
|
26
|
+
return accounts.value
|
27
|
+
}
|
20
28
|
|
21
29
|
return {
|
22
30
|
account,
|
23
|
-
|
31
|
+
accounts,
|
32
|
+
getAccount,
|
33
|
+
getAccounts
|
24
34
|
}
|
25
35
|
})
|
@@ -1,3 +1,11 @@
|
|
1
|
-
import { Queries } from '@coopenomics/sdk';
|
1
|
+
import { Queries, Zeus } from '@coopenomics/sdk';
|
2
2
|
|
3
3
|
export type IAccount = Queries.Accounts.GetAccount.IOutput[typeof Queries.Accounts.GetAccount.name]
|
4
|
+
export type IAccounts = Queries.Accounts.GetAccounts.IOutput[typeof Queries.Accounts.GetAccounts.name]
|
5
|
+
export type IGetAccounts = {data?: Queries.Accounts.GetAccounts.IInput['data'], options?: Queries.Accounts.GetAccounts.IInput['options']}
|
6
|
+
|
7
|
+
export const AccountTypes = Zeus.AccountType
|
8
|
+
|
9
|
+
export type IIndividualData = Zeus.ModelTypes['Individual']
|
10
|
+
export type IOrganizationData = Zeus.ModelTypes['Organization']
|
11
|
+
export type IEntrepreneurData = Zeus.ModelTypes['Entrepreneur']
|
@@ -57,8 +57,6 @@ export const useSessionStore = defineStore('session', (): ISessionStore => {
|
|
57
57
|
await globalStore.init();
|
58
58
|
isAuth.value = globalStore.hasCreditials;
|
59
59
|
|
60
|
-
//TODO добавить более детальную проверку авторизации
|
61
|
-
|
62
60
|
getInfo();
|
63
61
|
|
64
62
|
try {
|
@@ -80,7 +78,8 @@ export const useSessionStore = defineStore('session', (): ISessionStore => {
|
|
80
78
|
} catch (e: any) {
|
81
79
|
console.error(e);
|
82
80
|
FailAlert(e.message);
|
83
|
-
|
81
|
+
close()
|
82
|
+
globalStore.logout()
|
84
83
|
}
|
85
84
|
}
|
86
85
|
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { client } from 'src/shared/api/client'
|
2
|
+
import { Mutations } from '@coopenomics/sdk'
|
3
|
+
|
4
|
+
async function updateAccount(data: Mutations.Accounts.UpdateAccount.IInput['data']): Promise<Mutations.Accounts.UpdateAccount.IOutput[[typeof Mutations.Accounts.UpdateAccount.name][number]]>{
|
5
|
+
const {[Mutations.Accounts.UpdateAccount.name]: result} = await client.Mutation(Mutations.Accounts.UpdateAccount.mutation, {variables: {
|
6
|
+
data
|
7
|
+
}})
|
8
|
+
|
9
|
+
return result
|
10
|
+
}
|
11
|
+
|
12
|
+
export const api = {
|
13
|
+
updateAccount
|
14
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './model'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import type { Mutations } from '@coopenomics/sdk';
|
2
|
+
import { api } from '../api'
|
3
|
+
import { IAccount } from 'src/entities/Account/types';
|
4
|
+
|
5
|
+
export type IUpdateAccountInput = Mutations.Accounts.UpdateAccount.IInput['data']
|
6
|
+
|
7
|
+
export function useUpdateAccount() {
|
8
|
+
|
9
|
+
async function updateAccount(data: IUpdateAccountInput): Promise<IAccount> {
|
10
|
+
const account = await api.updateAccount(data);
|
11
|
+
|
12
|
+
return account;
|
13
|
+
}
|
14
|
+
|
15
|
+
return { updateAccount };
|
16
|
+
}
|
@@ -60,8 +60,9 @@ div
|
|
60
60
|
}
|
61
61
|
|
62
62
|
const back = () => step.value--
|
63
|
-
|
64
|
-
|
63
|
+
|
64
|
+
if (session.isAuth)
|
65
|
+
branchStore.loadPublicBranches({ coopname: system.info.coopname })
|
65
66
|
|
66
67
|
const generate = async () => {
|
67
68
|
isLoading.value = true
|
@@ -99,6 +99,7 @@ import { useSystemStore } from 'src/entities/System/model';
|
|
99
99
|
const { info } = useSystemStore()
|
100
100
|
|
101
101
|
import { notEmpty } from 'src/shared/lib/utils';
|
102
|
+
import { useAccountStore } from 'src/entities/Account/model';
|
102
103
|
|
103
104
|
const { state, addUserState, clearUserData } = useRegistratorStore()
|
104
105
|
const spread_minimum = ref(true) //TODO REPLACE IT!
|
@@ -204,6 +205,7 @@ const addUserNow = (userDataForm: any) => {
|
|
204
205
|
userDataForm.validate().then(async (success: boolean) => {
|
205
206
|
if (success) {
|
206
207
|
try {
|
208
|
+
const accountStore = useAccountStore()
|
207
209
|
loading.value = true
|
208
210
|
await addUser()
|
209
211
|
SuccessAlert('Пайщик добавлен в реестр, а приглашение отправлено на его email');
|
@@ -211,6 +213,7 @@ const addUserNow = (userDataForm: any) => {
|
|
211
213
|
addUserState.created_at = ''
|
212
214
|
clearUserData()
|
213
215
|
loading.value = false
|
216
|
+
accountStore.getAccounts()
|
214
217
|
} catch (e: any) {
|
215
218
|
loading.value = false
|
216
219
|
FailAlert(`Возникла ошибка: ${e.message}`)
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import { useSessionStore } from 'src/entities/Session'
|
2
2
|
import { useGlobalStore } from 'src/shared/store'
|
3
|
-
import { api } from '../api'
|
3
|
+
// import { api } from '../api'
|
4
4
|
import { useCurrentUserStore } from 'src/entities/User'
|
5
5
|
|
6
6
|
export function useLogoutUser() {
|
7
7
|
async function logout(): Promise<void> {
|
8
8
|
const global = useGlobalStore()
|
9
|
-
|
10
|
-
if (global.tokens?.refresh.token) await api.logoutUser(global.tokens?.refresh.token)
|
9
|
+
//TODO: "начать с начала" при регистрации бажит на это - да и в целом пора бы маршруты срезать эти
|
10
|
+
// if (global.tokens?.refresh.token) await api.logoutUser(global.tokens?.refresh.token)
|
11
11
|
|
12
12
|
await global.logout()
|
13
13
|
|
@@ -53,11 +53,9 @@ div
|
|
53
53
|
BankDetailsCard(:bankDetails="props.row.bank_account")
|
54
54
|
div.col-md-4.col-xs-12.q-pa-sm
|
55
55
|
p.text-center.text-overline карточка председателя
|
56
|
-
|
56
|
+
EditableIndividualCard(:participantData="props.row.trustee" :readonly="true").q-mt-sm
|
57
57
|
div.text-wrap
|
58
58
|
p.text-grey для замены председателя участка - измените его имя аккаунта в карточке участка на аккаунт одного из пайщиков.
|
59
|
-
|
60
|
-
|
61
59
|
</template>
|
62
60
|
|
63
61
|
<script lang="ts" setup>
|
@@ -67,7 +65,7 @@ import { useEditableTableRows } from 'src/shared/lib/composables/useEditableTabl
|
|
67
65
|
import { CreateBranchButton } from 'src/features/Branch/CreateBranch';
|
68
66
|
import { getNameFromUserData } from 'src/shared/lib/utils/getNameFromUserData';
|
69
67
|
import { BranchCard } from 'src/widgets/BranchCard';
|
70
|
-
import {
|
68
|
+
import { EditableIndividualCard } from 'src/shared/ui/EditableIndividualCard';
|
71
69
|
import { BankDetailsCard } from 'src/widgets/BankDetailsCard';
|
72
70
|
import { useSystemStore } from 'src/entities/System/model';
|
73
71
|
const { info } = useSystemStore()
|
@@ -21,7 +21,7 @@ export function useEditableData<T extends Record<string, any>>(
|
|
21
21
|
|
22
22
|
// Автоматически отслеживаем изменения в editableData
|
23
23
|
watch(
|
24
|
-
|
24
|
+
editableData,
|
25
25
|
(newData) => {
|
26
26
|
isEditing.value = !isEqual(newData.value, originalData.value); // Глубокое сравнение объектов
|
27
27
|
validateForm(); // Проверка валидности формы при изменении данных
|
@@ -0,0 +1,170 @@
|
|
1
|
+
<template lang="pug">
|
2
|
+
q-form(ref="form")
|
3
|
+
q-input(
|
4
|
+
dense
|
5
|
+
v-model="data.last_name"
|
6
|
+
standout="bg-teal text-white"
|
7
|
+
label="Фамилия"
|
8
|
+
:readonly="readonly"
|
9
|
+
:rules="[val => notEmpty(val), val => validatePersonalName(val)]"
|
10
|
+
autocomplete="off"
|
11
|
+
)
|
12
|
+
q-input(
|
13
|
+
dense
|
14
|
+
v-model="data.first_name"
|
15
|
+
standout="bg-teal text-white"
|
16
|
+
label="Имя"
|
17
|
+
:readonly="readonly"
|
18
|
+
:rules="[val => notEmpty(val), val => validatePersonalName(val)]"
|
19
|
+
autocomplete="off"
|
20
|
+
)
|
21
|
+
q-input(
|
22
|
+
dense
|
23
|
+
v-model="data.middle_name"
|
24
|
+
standout="bg-teal text-white"
|
25
|
+
label="Отчество"
|
26
|
+
:readonly="readonly"
|
27
|
+
:rules="[val => validatePersonalName(val)]"
|
28
|
+
autocomplete="off"
|
29
|
+
)
|
30
|
+
|
31
|
+
q-input(
|
32
|
+
dense
|
33
|
+
v-model="data.birthdate"
|
34
|
+
standout="bg-teal text-white"
|
35
|
+
mask="date"
|
36
|
+
label="Дата рождения"
|
37
|
+
placeholder="Формат: год/месяц/день"
|
38
|
+
:readonly="readonly"
|
39
|
+
:rules="['date', val => notEmpty(val)]"
|
40
|
+
autocomplete="off"
|
41
|
+
)
|
42
|
+
template(v-slot:append)
|
43
|
+
q-icon(name="event" class="cursor-pointer" v-if="!readonly")
|
44
|
+
q-popup-proxy(cover transition-show="scale" transition-hide="scale")
|
45
|
+
q-date(v-model="data.birthdate")
|
46
|
+
.row.items-center.justify-end
|
47
|
+
q-btn(v-close-popup label="Закрыть" color="primary" flat)
|
48
|
+
|
49
|
+
q-input(
|
50
|
+
dense
|
51
|
+
v-model="data.phone"
|
52
|
+
standout="bg-teal text-white"
|
53
|
+
label="Номер телефона"
|
54
|
+
mask="+7 (###) ###-##-##"
|
55
|
+
fill-mask
|
56
|
+
:readonly="readonly"
|
57
|
+
:rules="[val => notEmpty(val), val => notEmptyPhone(val)]"
|
58
|
+
autocomplete="off"
|
59
|
+
)
|
60
|
+
|
61
|
+
q-select(
|
62
|
+
dense
|
63
|
+
v-model="data.country"
|
64
|
+
standout="bg-teal text-white"
|
65
|
+
label="Страна"
|
66
|
+
:options="[{ label: 'Российская Федерация', value: 'Российская Федерация' }]"
|
67
|
+
map-options
|
68
|
+
emit-value
|
69
|
+
:readonly="readonly"
|
70
|
+
:rules="[val => notEmpty(val)]"
|
71
|
+
)
|
72
|
+
|
73
|
+
q-input(
|
74
|
+
dense
|
75
|
+
v-model="data.city"
|
76
|
+
standout="bg-teal text-white"
|
77
|
+
label="Город"
|
78
|
+
:readonly="readonly"
|
79
|
+
:rules="[val => notEmpty(val)]"
|
80
|
+
autocomplete="off"
|
81
|
+
)
|
82
|
+
|
83
|
+
q-input(
|
84
|
+
dense
|
85
|
+
v-model="data.full_address"
|
86
|
+
standout="bg-teal text-white"
|
87
|
+
label="Адрес регистрации"
|
88
|
+
:readonly="readonly"
|
89
|
+
:rules="[val => notEmpty(val)]"
|
90
|
+
autocomplete="off"
|
91
|
+
)
|
92
|
+
|
93
|
+
q-input(
|
94
|
+
dense
|
95
|
+
v-model="data.details.inn"
|
96
|
+
standout="bg-teal text-white"
|
97
|
+
mask="############"
|
98
|
+
label="ИНН предпринимателя"
|
99
|
+
:readonly="readonly"
|
100
|
+
:rules="[val => notEmpty(val), val => (val.length === 10 || val.length === 12) || 'ИНН должен содержать 10 или 12 цифр']"
|
101
|
+
autocomplete="off"
|
102
|
+
)
|
103
|
+
|
104
|
+
q-input(
|
105
|
+
dense
|
106
|
+
v-model="data.details.ogrn"
|
107
|
+
standout="bg-teal text-white"
|
108
|
+
mask="###############"
|
109
|
+
label="ОГРНИП"
|
110
|
+
:readonly="readonly"
|
111
|
+
:rules="[val => notEmpty(val), val => (val.length === 13 || val.length === 15) || 'ОГРНИП должен содержать 13 или 15 цифр']"
|
112
|
+
autocomplete="off"
|
113
|
+
)
|
114
|
+
|
115
|
+
EditableActions(
|
116
|
+
v-if="!readonly"
|
117
|
+
:isEditing="isEditing"
|
118
|
+
:isDisabled="isDisabled"
|
119
|
+
@save="saveChanges"
|
120
|
+
@cancel="cancelChanges"
|
121
|
+
)
|
122
|
+
</template>
|
123
|
+
|
124
|
+
<script setup lang="ts">
|
125
|
+
import { ref } from 'vue';
|
126
|
+
import { useEditableData } from 'src/shared/lib/composables/useEditableData';
|
127
|
+
import { notEmpty, notEmptyPhone, validatePersonalName } from 'src/shared/lib/utils';
|
128
|
+
import { failAlert, SuccessAlert } from 'src/shared/api';
|
129
|
+
import { EditableActions } from 'src/shared/ui/EditableActions';
|
130
|
+
import { type IUpdateAccountInput, useUpdateAccount } from 'src/features/Account/UpdateAccount/model';
|
131
|
+
import { type IEntrepreneurData } from 'src/entities/Account/types';
|
132
|
+
|
133
|
+
const emit = defineEmits(['update']);
|
134
|
+
const { updateAccount } = useUpdateAccount();
|
135
|
+
|
136
|
+
const props = defineProps({
|
137
|
+
participantData: {
|
138
|
+
type: Object as () => IEntrepreneurData,
|
139
|
+
required: true
|
140
|
+
},
|
141
|
+
readonly: {
|
142
|
+
type: Boolean,
|
143
|
+
default: false
|
144
|
+
}
|
145
|
+
});
|
146
|
+
|
147
|
+
const localEntrepreneurData = ref(props.participantData);
|
148
|
+
const form = ref();
|
149
|
+
|
150
|
+
const handleSave = async () => {
|
151
|
+
try {
|
152
|
+
const account_data: IUpdateAccountInput = {
|
153
|
+
username: props.participantData.username,
|
154
|
+
entrepreneur_data: data.value,
|
155
|
+
};
|
156
|
+
await updateAccount(account_data);
|
157
|
+
emit('update', JSON.parse(JSON.stringify(data.value)));
|
158
|
+
SuccessAlert('Данные аккаунта обновлены');
|
159
|
+
} catch (e) {
|
160
|
+
console.log(e);
|
161
|
+
failAlert(e);
|
162
|
+
}
|
163
|
+
};
|
164
|
+
|
165
|
+
const { editableData: data, isEditing, isDisabled, saveChanges, cancelChanges } = useEditableData(
|
166
|
+
localEntrepreneurData.value,
|
167
|
+
handleSave,
|
168
|
+
form
|
169
|
+
);
|
170
|
+
</script>
|
@@ -0,0 +1 @@
|
|
1
|
+
export {default as EditableEntrepreneurCard} from './EditableEntrepreneurCard.vue'
|
@@ -0,0 +1,203 @@
|
|
1
|
+
<template lang="pug">
|
2
|
+
q-form(ref="form" v-if="data")
|
3
|
+
q-input(
|
4
|
+
dense
|
5
|
+
v-model="data.email"
|
6
|
+
standout="bg-teal text-white"
|
7
|
+
label="Email"
|
8
|
+
:readonly="readonly"
|
9
|
+
:rules="[val => validEmail(val)]"
|
10
|
+
autocomplete="off"
|
11
|
+
)
|
12
|
+
q-input(
|
13
|
+
dense
|
14
|
+
v-model="data.first_name"
|
15
|
+
standout="bg-teal text-white"
|
16
|
+
label="Имя"
|
17
|
+
:readonly="readonly"
|
18
|
+
:rules="[val => notEmpty(val)]"
|
19
|
+
autocomplete="off"
|
20
|
+
)
|
21
|
+
|
22
|
+
q-input(
|
23
|
+
dense
|
24
|
+
v-model="data.middle_name"
|
25
|
+
standout="bg-teal text-white"
|
26
|
+
label="Отчество"
|
27
|
+
:readonly="readonly"
|
28
|
+
:rules="[val => validatePersonalName(val)]"
|
29
|
+
autocomplete="off"
|
30
|
+
)
|
31
|
+
|
32
|
+
q-input(
|
33
|
+
dense
|
34
|
+
v-model="data.last_name"
|
35
|
+
standout="bg-teal text-white"
|
36
|
+
label="Фамилия"
|
37
|
+
:readonly="readonly"
|
38
|
+
:rules="[val => notEmpty(val), val => validatePersonalName(val)]"
|
39
|
+
autocomplete="off"
|
40
|
+
)
|
41
|
+
|
42
|
+
q-input(
|
43
|
+
dense
|
44
|
+
v-model="data.birthdate"
|
45
|
+
standout="bg-teal text-white"
|
46
|
+
mask="date"
|
47
|
+
label="Дата рождения"
|
48
|
+
placeholder="Формат: год/месяц/день"
|
49
|
+
:readonly="readonly"
|
50
|
+
:rules="['date', val => notEmpty(val)]"
|
51
|
+
autocomplete="off"
|
52
|
+
)
|
53
|
+
template(v-slot:append)
|
54
|
+
q-icon(name="event" class="cursor-pointer" v-if="!readonly")
|
55
|
+
q-popup-proxy(cover transition-show="scale" transition-hide="scale")
|
56
|
+
q-date(v-model="data.birthdate")
|
57
|
+
.row.items-center.justify-end
|
58
|
+
q-btn(v-close-popup label="Закрыть" color="primary" flat)
|
59
|
+
|
60
|
+
q-input(
|
61
|
+
dense
|
62
|
+
v-model="data.phone"
|
63
|
+
standout="bg-teal text-white"
|
64
|
+
label="Телефон"
|
65
|
+
:readonly="readonly"
|
66
|
+
:rules="[val => notEmpty(val)]"
|
67
|
+
autocomplete="off"
|
68
|
+
)
|
69
|
+
|
70
|
+
div(v-if="data.passport")
|
71
|
+
q-input(
|
72
|
+
dense
|
73
|
+
v-model="data.passport.code"
|
74
|
+
standout="bg-teal text-white"
|
75
|
+
label="Код подразделения"
|
76
|
+
mask="###-###"
|
77
|
+
:readonly="readonly"
|
78
|
+
:rules="[val => notEmpty(val), val => val.length === 7 || 'Код подразделения состоит из 6 цифр и тире (xxx-xxx)']"
|
79
|
+
autocomplete="off"
|
80
|
+
)
|
81
|
+
|
82
|
+
q-input(
|
83
|
+
dense
|
84
|
+
:model-value="data.passport.series"
|
85
|
+
@update:model-value="val => data.passport.series = Number(val)"
|
86
|
+
standout="bg-teal text-white"
|
87
|
+
label="Серия паспорта"
|
88
|
+
mask="####"
|
89
|
+
type="text"
|
90
|
+
:readonly="readonly"
|
91
|
+
:rules="[val => notEmpty(val), val => String(val).length === 4 || 'Серия должна состоять из 4 цифр']"
|
92
|
+
autocomplete="off"
|
93
|
+
)
|
94
|
+
|
95
|
+
q-input(
|
96
|
+
dense
|
97
|
+
:model-value="data.passport.number"
|
98
|
+
@update:model-value="val => data.passport.number = Number(val)"
|
99
|
+
standout="bg-teal text-white"
|
100
|
+
label="Номер паспорта"
|
101
|
+
mask="######"
|
102
|
+
type="text"
|
103
|
+
:readonly="readonly"
|
104
|
+
:rules="[val => notEmpty(val), val => String(val).length === 6 || 'Номер паспорта должен состоять из 6 цифр']"
|
105
|
+
autocomplete="off"
|
106
|
+
)
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
q-input(
|
111
|
+
dense
|
112
|
+
v-model="data.passport.issued_at"
|
113
|
+
standout="bg-teal text-white"
|
114
|
+
mask="date"
|
115
|
+
label="Дата выдачи"
|
116
|
+
:readonly="readonly"
|
117
|
+
:rules="['date', val => notEmpty(val)]"
|
118
|
+
autocomplete="off"
|
119
|
+
)
|
120
|
+
template(v-slot:append)
|
121
|
+
q-icon(name="event" class="cursor-pointer" v-if="!readonly")
|
122
|
+
q-popup-proxy(cover transition-show="scale" transition-hide="scale")
|
123
|
+
q-date(v-model="data.passport.issued_at")
|
124
|
+
.row.items-center.justify-end
|
125
|
+
q-btn(v-close-popup label="Закрыть" color="primary" flat)
|
126
|
+
|
127
|
+
q-input(
|
128
|
+
dense
|
129
|
+
v-model="data.passport.issued_by"
|
130
|
+
standout="bg-teal text-white"
|
131
|
+
label="Кем выдан"
|
132
|
+
:readonly="readonly"
|
133
|
+
:rules="[val => notEmpty(val)]"
|
134
|
+
autocomplete="off"
|
135
|
+
)
|
136
|
+
|
137
|
+
q-input(
|
138
|
+
dense
|
139
|
+
v-model="data.full_address"
|
140
|
+
standout="bg-teal text-white"
|
141
|
+
label="Адрес регистрации"
|
142
|
+
:readonly="readonly"
|
143
|
+
:rules="[val => notEmpty(val)]"
|
144
|
+
autocomplete="off"
|
145
|
+
)
|
146
|
+
|
147
|
+
EditableActions(
|
148
|
+
v-if="!readonly"
|
149
|
+
:isEditing="isEditing"
|
150
|
+
:isDisabled="isDisabled"
|
151
|
+
@save="saveChanges"
|
152
|
+
@cancel="cancelChanges"
|
153
|
+
)
|
154
|
+
</template>
|
155
|
+
|
156
|
+
<script lang="ts" setup>
|
157
|
+
import { ref } from 'vue';
|
158
|
+
import { useEditableData } from 'src/shared/lib/composables/useEditableData';
|
159
|
+
import { validEmail } from 'src/shared/lib/utils/validEmailRule';
|
160
|
+
import { validatePersonalName, notEmpty } from 'src/shared/lib/utils';
|
161
|
+
import { failAlert, SuccessAlert } from 'src/shared/api';
|
162
|
+
import { type IUpdateAccountInput, useUpdateAccount } from 'src/features/Account/UpdateAccount/model';
|
163
|
+
import { EditableActions } from 'src/shared/ui/EditableActions';
|
164
|
+
import { type IIndividualData } from 'src/entities/Account/types';
|
165
|
+
|
166
|
+
const { updateAccount } = useUpdateAccount()
|
167
|
+
|
168
|
+
const props = defineProps({
|
169
|
+
participantData: {
|
170
|
+
type: Object as () => IIndividualData,
|
171
|
+
required: true
|
172
|
+
},
|
173
|
+
readonly: {
|
174
|
+
type: Boolean,
|
175
|
+
default: false
|
176
|
+
}
|
177
|
+
});
|
178
|
+
|
179
|
+
const localParticipantData = ref(props.participantData);
|
180
|
+
const form = ref();
|
181
|
+
const emit = defineEmits(['update']);
|
182
|
+
|
183
|
+
const handleSave = async () => {
|
184
|
+
try {
|
185
|
+
const account_data: IUpdateAccountInput = {
|
186
|
+
username: props.participantData.username,
|
187
|
+
individual_data: data.value,
|
188
|
+
}
|
189
|
+
await updateAccount(account_data);
|
190
|
+
emit('update', JSON.parse(JSON.stringify(data.value)))
|
191
|
+
SuccessAlert('Данные аккаунта обновлены')
|
192
|
+
} catch (e: any) {
|
193
|
+
failAlert(e);
|
194
|
+
}
|
195
|
+
};
|
196
|
+
|
197
|
+
const { editableData: data, isEditing, isDisabled, saveChanges, cancelChanges } = useEditableData(
|
198
|
+
localParticipantData.value,
|
199
|
+
handleSave,
|
200
|
+
form
|
201
|
+
);
|
202
|
+
</script>
|
203
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
export {default as EditableIndividualCard} from './EditableIndividualCard.vue'
|
@@ -0,0 +1,215 @@
|
|
1
|
+
<template lang="pug">
|
2
|
+
q-form(ref="form" v-if="data")
|
3
|
+
q-select(
|
4
|
+
dense
|
5
|
+
v-model="data.type"
|
6
|
+
standout="bg-teal text-white"
|
7
|
+
label="Тип организации"
|
8
|
+
:options="[{ label: 'Потребительский Кооператив', value: 'coop' }, { label: 'Производственный Кооператив', value: 'prodcoop' }, { label: 'ООО', value: 'ooo' }]"
|
9
|
+
emit-value
|
10
|
+
map-options
|
11
|
+
:rules="[val => notEmpty(val)]"
|
12
|
+
:readonly="readonly"
|
13
|
+
)
|
14
|
+
|
15
|
+
q-input(
|
16
|
+
dense
|
17
|
+
v-model="data.short_name"
|
18
|
+
standout="bg-teal text-white"
|
19
|
+
label="Краткое наименование"
|
20
|
+
:rules="[val => notEmpty(val)]"
|
21
|
+
:readonly="readonly"
|
22
|
+
autocomplete="off"
|
23
|
+
)
|
24
|
+
q-input(
|
25
|
+
dense
|
26
|
+
v-model="data.full_name"
|
27
|
+
standout="bg-teal text-white"
|
28
|
+
label="Полное наименование"
|
29
|
+
:rules="[val => notEmpty(val)]"
|
30
|
+
:readonly="readonly"
|
31
|
+
autocomplete="off"
|
32
|
+
)
|
33
|
+
|
34
|
+
q-input(
|
35
|
+
dense
|
36
|
+
v-model="data.represented_by.last_name"
|
37
|
+
standout="bg-teal text-white"
|
38
|
+
label="Фамилия представителя"
|
39
|
+
:rules="[val => notEmpty(val), val => validatePersonalName(val)]"
|
40
|
+
:readonly="readonly"
|
41
|
+
autocomplete="off"
|
42
|
+
)
|
43
|
+
q-input(
|
44
|
+
dense
|
45
|
+
v-model="data.represented_by.first_name"
|
46
|
+
standout="bg-teal text-white"
|
47
|
+
label="Имя представителя"
|
48
|
+
:rules="[val => notEmpty(val), val => validatePersonalName(val)]"
|
49
|
+
:readonly="readonly"
|
50
|
+
autocomplete="off"
|
51
|
+
)
|
52
|
+
q-input(
|
53
|
+
dense
|
54
|
+
v-model="data.represented_by.middle_name"
|
55
|
+
standout="bg-teal text-white"
|
56
|
+
label="Отчество представителя"
|
57
|
+
:rules="[val => validatePersonalName(val)]"
|
58
|
+
:readonly="readonly"
|
59
|
+
autocomplete="off"
|
60
|
+
)
|
61
|
+
q-input(
|
62
|
+
dense
|
63
|
+
v-model="data.represented_by.based_on"
|
64
|
+
standout="bg-teal text-white"
|
65
|
+
label="На основании"
|
66
|
+
:rules="[val => notEmpty(val)]"
|
67
|
+
:readonly="readonly"
|
68
|
+
autocomplete="off"
|
69
|
+
)
|
70
|
+
q-input(
|
71
|
+
dense
|
72
|
+
v-model="data.represented_by.position"
|
73
|
+
standout="bg-teal text-white"
|
74
|
+
label="Должность представителя"
|
75
|
+
:rules="[val => notEmpty(val)]"
|
76
|
+
:readonly="readonly"
|
77
|
+
autocomplete="off"
|
78
|
+
)
|
79
|
+
|
80
|
+
q-input(
|
81
|
+
dense
|
82
|
+
v-model="data.phone"
|
83
|
+
standout="bg-teal text-white"
|
84
|
+
label="Телефон"
|
85
|
+
mask="+7 (###) ###-##-##"
|
86
|
+
fill-mask
|
87
|
+
:rules="[val => notEmpty(val), val => notEmptyPhone(val)]"
|
88
|
+
:readonly="readonly"
|
89
|
+
autocomplete="off"
|
90
|
+
)
|
91
|
+
|
92
|
+
q-input(
|
93
|
+
dense
|
94
|
+
v-model="data.country"
|
95
|
+
standout="bg-teal text-white"
|
96
|
+
label="Страна"
|
97
|
+
:rules="[val => notEmpty(val)]"
|
98
|
+
:readonly="readonly"
|
99
|
+
autocomplete="off"
|
100
|
+
)
|
101
|
+
q-input(
|
102
|
+
dense
|
103
|
+
v-model="data.city"
|
104
|
+
standout="bg-teal text-white"
|
105
|
+
label="Город"
|
106
|
+
:rules="[val => notEmpty(val)]"
|
107
|
+
:readonly="readonly"
|
108
|
+
autocomplete="off"
|
109
|
+
)
|
110
|
+
q-input(
|
111
|
+
dense
|
112
|
+
v-model="data.full_address"
|
113
|
+
standout="bg-teal text-white"
|
114
|
+
label="Юридический адрес"
|
115
|
+
:rules="[val => notEmpty(val)]"
|
116
|
+
:readonly="readonly"
|
117
|
+
autocomplete="off"
|
118
|
+
)
|
119
|
+
q-input(
|
120
|
+
dense
|
121
|
+
v-model="data.fact_address"
|
122
|
+
standout="bg-teal text-white"
|
123
|
+
label="Фактический адрес"
|
124
|
+
:rules="[val => notEmpty(val)]"
|
125
|
+
:readonly="readonly"
|
126
|
+
autocomplete="off"
|
127
|
+
)
|
128
|
+
|
129
|
+
q-input(
|
130
|
+
dense
|
131
|
+
v-model="data.details.inn"
|
132
|
+
standout="bg-teal text-white"
|
133
|
+
mask="############"
|
134
|
+
label="ИНН"
|
135
|
+
:rules="[val => notEmpty(val), val => (val.length === 10 || val.length === 12) || 'ИНН должен содержать 10 или 12 цифр']"
|
136
|
+
:readonly="readonly"
|
137
|
+
autocomplete="off"
|
138
|
+
)
|
139
|
+
q-input(
|
140
|
+
dense
|
141
|
+
v-model="data.details.ogrn"
|
142
|
+
standout="bg-teal text-white"
|
143
|
+
mask="###############"
|
144
|
+
label="ОГРН"
|
145
|
+
:rules="[val => notEmpty(val), val => (val.length === 13 || val.length === 15) || 'ОГРН должен содержать 13 или 15 цифр']"
|
146
|
+
:readonly="readonly"
|
147
|
+
autocomplete="off"
|
148
|
+
)
|
149
|
+
q-input(
|
150
|
+
dense
|
151
|
+
v-model="data.details.kpp"
|
152
|
+
standout="bg-teal text-white"
|
153
|
+
mask="#########"
|
154
|
+
label="КПП"
|
155
|
+
:rules="[val => notEmpty(val), val => val.length === 9 || 'КПП должен содержать 9 цифр']"
|
156
|
+
:readonly="readonly"
|
157
|
+
autocomplete="off"
|
158
|
+
)
|
159
|
+
|
160
|
+
EditableActions(
|
161
|
+
v-if="!readonly"
|
162
|
+
:isEditing="isEditing"
|
163
|
+
:isDisabled="isDisabled"
|
164
|
+
@save="saveChanges"
|
165
|
+
@cancel="cancelChanges"
|
166
|
+
)
|
167
|
+
</template>
|
168
|
+
|
169
|
+
<script lang="ts" setup>
|
170
|
+
import { ref } from 'vue';
|
171
|
+
import { useEditableData } from 'src/shared/lib/composables/useEditableData';
|
172
|
+
import { notEmpty, notEmptyPhone, validatePersonalName } from 'src/shared/lib/utils';
|
173
|
+
import { failAlert, SuccessAlert } from 'src/shared/api';
|
174
|
+
import { EditableActions } from 'src/shared/ui/EditableActions';
|
175
|
+
import { type IUpdateAccountInput, useUpdateAccount } from 'src/features/Account/UpdateAccount/model';
|
176
|
+
import { type IOrganizationData } from 'src/entities/Account/types';
|
177
|
+
|
178
|
+
const emit = defineEmits(['update']);
|
179
|
+
const { updateAccount } = useUpdateAccount();
|
180
|
+
|
181
|
+
const props = defineProps({
|
182
|
+
participantData: {
|
183
|
+
type: Object as () => IOrganizationData,
|
184
|
+
required: true
|
185
|
+
},
|
186
|
+
readonly: {
|
187
|
+
type: Boolean,
|
188
|
+
default: false
|
189
|
+
}
|
190
|
+
});
|
191
|
+
|
192
|
+
const localOrganizationData = ref(props.participantData);
|
193
|
+
const form = ref();
|
194
|
+
|
195
|
+
const handleSave = async () => {
|
196
|
+
try {
|
197
|
+
const account_data: IUpdateAccountInput = {
|
198
|
+
username: props.participantData.username,
|
199
|
+
organization_data: data.value,
|
200
|
+
};
|
201
|
+
await updateAccount(account_data);
|
202
|
+
emit('update', JSON.parse(JSON.stringify(data.value)));
|
203
|
+
SuccessAlert('Данные аккаунта обновлены');
|
204
|
+
} catch (e) {
|
205
|
+
console.log(e);
|
206
|
+
failAlert(e);
|
207
|
+
}
|
208
|
+
};
|
209
|
+
const { editableData: data, isEditing, isDisabled, saveChanges, cancelChanges } = useEditableData(
|
210
|
+
localOrganizationData.value,
|
211
|
+
handleSave,
|
212
|
+
form
|
213
|
+
);
|
214
|
+
</script>
|
215
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
export {default as EditableOrganizationCard} from './EditableOrganizationCard.vue'
|
@@ -7,6 +7,7 @@ div(v-if="userData.organization_data").q-gutter-sm.q-mt-md
|
|
7
7
|
:options="[{ label: 'Потребительский Кооператив', value: 'coop' }, { label: 'Производственный Кооператив', value: 'prodcoop' }, { label: 'ООО', value: 'ooo' }]"
|
8
8
|
emit-value
|
9
9
|
map-options).q-mb-md
|
10
|
+
|
10
11
|
q-input(v-model="userData.organization_data.short_name" standout="bg-teal text-white" hint="ПК Ромашка" label="Краткое наименование организации" :rules="[val => notEmpty(val)]" autocomplete="off")
|
11
12
|
q-input(v-model="userData.organization_data.full_name" standout="bg-teal text-white" hint="Потребительский Кооператив 'Ромашка'" label="Полное наименование организации" :rules="[val => notEmpty(val)]" autocomplete="off")
|
12
13
|
q-input(v-model="userData.organization_data.represented_by.last_name" standout="bg-teal text-white" label="Фамилия представителя" hint="" :rules="[val => notEmpty(val), val => validatePersonalName(val)]" autocomplete="off")
|
package/src/widgets/Cooperative/Participants/ListOfParticipants/ui/ListOfParticipantsWidget.vue
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
<template lang="pug">
|
2
|
+
|
2
3
|
q-table(
|
3
|
-
ref="tableRef"
|
4
|
+
ref="tableRef"
|
4
5
|
flat
|
5
|
-
:rows="
|
6
|
+
:rows="accountStore.accounts.items"
|
6
7
|
:columns="columns"
|
7
|
-
:table-colspan="9"
|
8
8
|
row-key="username"
|
9
9
|
:pagination="pagination"
|
10
10
|
virtual-scroll
|
@@ -15,12 +15,10 @@ q-table(
|
|
15
15
|
).full-height
|
16
16
|
template(#top)
|
17
17
|
slot(name="top")
|
18
|
-
|
18
|
+
|
19
19
|
template(#header="props")
|
20
|
-
|
21
20
|
q-tr(:props="props")
|
22
21
|
q-th(auto-width)
|
23
|
-
|
24
22
|
q-th(
|
25
23
|
v-for="col in props.cols"
|
26
24
|
:key="col.name"
|
@@ -28,76 +26,161 @@ q-table(
|
|
28
26
|
) {{ col.label }}
|
29
27
|
|
30
28
|
template(#body="props")
|
29
|
+
|
31
30
|
q-tr(:key="`m_${props.row.username}`" :props="props")
|
32
31
|
q-td(auto-width)
|
33
|
-
|
34
|
-
|
32
|
+
q-btn(
|
33
|
+
size="sm"
|
34
|
+
color="primary"
|
35
|
+
round
|
36
|
+
dense
|
37
|
+
:icon="expanded.get(props.row.username) ? 'remove' : 'add'"
|
38
|
+
@click="toggleExpand(props.row.username)"
|
39
|
+
)
|
40
|
+
|
41
|
+
q-td {{ getName(props.row) }}
|
42
|
+
q-td {{ props.row.private_account.type === AccountTypes.Individual ? 'физ. лицо' : (props.row.private_account.type === AccountTypes.Organization ? 'юр. лицо' : (props.row.private_account.type === AccountTypes.Entrepreneur ? 'инд. предприниматель' : 'неизвестно')) }}
|
35
43
|
q-td {{ props.row.username }}
|
36
|
-
q-td {{ props.row.
|
37
|
-
|
38
|
-
|
39
|
-
q-td
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
q-td {{ moment(props.row.blockchain_account?.created).format('DD.MM.YY HH:mm:ss') }}
|
45
|
+
|
46
|
+
q-tr(v-if="expanded.get(props.row.username)" :key="`e_${props.row.username}`" :props="props" class="q-virtual-scroll--with-prev")
|
47
|
+
q-td(colspan="100%" style="padding: 0 !important;")
|
48
|
+
q-tabs(
|
49
|
+
v-model="currentTab[props.row.username]"
|
50
|
+
align="justify"
|
51
|
+
stretch
|
52
|
+
dense
|
53
|
+
indicator-color="lime"
|
54
|
+
)
|
55
|
+
q-tab(name="info" label="Данные" class="bg-primary text-white")
|
56
|
+
q-tab(name="document" label="Документы" class="bg-primary text-white")
|
57
|
+
|
58
|
+
q-tab-panels(v-model="currentTab[props.row.username]" animated)
|
59
|
+
q-tab-panel(name="info")
|
60
|
+
component(:is="useComponent(props.row)" :participantData="usePrivateData(props.row)" @update="newData => update(props.row, newData)")
|
61
|
+
|
62
|
+
q-tab-panel(name="document")
|
63
|
+
slot(:expand="expanded.get(props.row.username)" :receiver="props.row.username")
|
64
|
+
|
49
65
|
</template>
|
50
|
-
|
51
66
|
<script setup lang="ts">
|
52
|
-
import { ref } from 'vue'
|
53
|
-
import { Notify } from 'quasar'
|
54
|
-
|
55
|
-
import {
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
import
|
60
|
-
|
61
|
-
const
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
}
|
67
|
+
import { ref, reactive } from 'vue'
|
68
|
+
import { Notify } from 'quasar'
|
69
|
+
import { EditableEntrepreneurCard } from 'src/shared/ui/EditableEntrepreneurCard';
|
70
|
+
import { EditableIndividualCard } from 'src/shared/ui/EditableIndividualCard';
|
71
|
+
import { EditableOrganizationCard } from 'src/shared/ui/EditableOrganizationCard';
|
72
|
+
import { useAccountStore } from 'src/entities/Account/model';
|
73
|
+
import moment from 'moment-with-locales-es6'
|
74
|
+
import { AccountTypes, type IAccount, type IIndividualData, type IOrganizationData, type IEntrepreneurData } from 'src/entities/Account/types';
|
75
|
+
|
76
|
+
const accountStore = useAccountStore()
|
77
|
+
|
78
|
+
const onLoading = ref(false)
|
79
|
+
|
80
|
+
const update = (
|
81
|
+
account: IAccount,
|
82
|
+
newData: IIndividualData | IOrganizationData | IEntrepreneurData
|
83
|
+
) => {
|
84
|
+
|
85
|
+
if (account.private_account?.type === AccountTypes.Individual) {
|
86
|
+
const individual = newData as IIndividualData;
|
87
|
+
account.private_account.individual_data = {
|
88
|
+
...individual,
|
89
|
+
passport: individual.passport ?? undefined, // заменяет null на undefined
|
90
|
+
};
|
91
|
+
} else if (account.private_account?.type === AccountTypes.Entrepreneur) {
|
92
|
+
account.private_account.entrepreneur_data = newData as IEntrepreneurData
|
93
|
+
} else if (account.private_account?.type === AccountTypes.Organization) {
|
94
|
+
account.private_account.organization_data = newData as IOrganizationData
|
95
|
+
}
|
96
|
+
|
97
|
+
};
|
98
|
+
|
99
|
+
|
100
|
+
// `Map` для отслеживания состояния раскрытых строк и вкладок
|
101
|
+
const expanded = reactive(new Map<string, boolean>())
|
102
|
+
const currentTab = reactive<Record<string, string>>({})
|
103
|
+
|
104
|
+
// Определяем, какой компонент использовать
|
105
|
+
const useComponent = (account: IAccount) => {
|
106
|
+
if (account.private_account)
|
107
|
+
switch (account.private_account.type) {
|
108
|
+
case AccountTypes.Individual:
|
109
|
+
return EditableIndividualCard
|
110
|
+
case AccountTypes.Entrepreneur:
|
111
|
+
return EditableEntrepreneurCard
|
112
|
+
case AccountTypes.Organization:
|
113
|
+
return EditableOrganizationCard
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
const usePrivateData = (account: IAccount) => {
|
118
|
+
if (account.private_account)
|
119
|
+
switch (account.private_account.type) {
|
120
|
+
case AccountTypes.Individual:
|
121
|
+
return account.private_account?.individual_data
|
122
|
+
case AccountTypes.Entrepreneur:
|
123
|
+
return account.private_account?.entrepreneur_data
|
124
|
+
case AccountTypes.Organization:
|
125
|
+
return account.private_account?.organization_data
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
// Загружаем данные
|
130
|
+
const loadParticipants = async () => {
|
131
|
+
try {
|
132
|
+
onLoading.value = true
|
133
|
+
|
134
|
+
await accountStore.getAccounts({options: {
|
135
|
+
page: 1,
|
136
|
+
limit: 1000,
|
137
|
+
sortOrder: 'DESC'
|
138
|
+
}})
|
139
|
+
|
140
|
+
onLoading.value = false
|
141
|
+
} catch (e: any) {
|
142
|
+
onLoading.value = false
|
143
|
+
Notify.create({
|
144
|
+
message: e.message,
|
145
|
+
type: 'negative',
|
146
|
+
})
|
147
|
+
}
|
76
148
|
}
|
77
|
-
}
|
78
|
-
|
79
|
-
loadParticipants()
|
80
|
-
|
81
|
-
const columns = [
|
82
|
-
{ name: 'username', align: 'left', label: 'Аккаунт', field: 'username', sortable: true },
|
83
|
-
{ name: 'last_name', align: 'left', label: 'Фамилия', field: 'last_name', sortable: true },
|
84
|
-
{ name: 'first_name', align: 'left', label: 'Имя', field: 'first_name', sortable: true },
|
85
|
-
{ name: 'middle_name', align: 'left', label: 'Отчество', field: 'middle_name', sortable: true },
|
86
|
-
|
87
|
-
{ name: 'phone', align: 'left', label: 'Телефон', field: 'phone', sortable: false },
|
88
|
-
{ name: 'email', align: 'left', label: 'Е-почта', field: 'email', sortable: false },
|
89
|
-
{ name: 'birthday', align: 'left', label: 'Дата рождения', field: 'birthday', sortable: true },
|
90
|
-
{
|
91
|
-
name: 'created_at',
|
92
|
-
align: 'left',
|
93
|
-
label: 'Зарегистрирован',
|
94
|
-
field: 'created_at',
|
95
|
-
sortable: true,
|
96
|
-
},
|
97
|
-
] as any
|
98
149
|
|
99
|
-
|
100
|
-
|
101
|
-
const
|
150
|
+
loadParticipants()
|
151
|
+
|
152
|
+
const columns = [
|
153
|
+
{ name: 'name', align: 'left', label: 'ФИО / Наименование', field: 'name', sortable: true },
|
154
|
+
{ name: 'type', align: 'left', label: 'Тип', field: 'type' },
|
155
|
+
{ name: 'username', align: 'left', label: 'Аккаунт', field: 'username', sortable: true },
|
156
|
+
{
|
157
|
+
name: 'created_at',
|
158
|
+
align: 'left',
|
159
|
+
label: 'Зарегистрирован',
|
160
|
+
field: 'created_at',
|
161
|
+
sortable: true,
|
162
|
+
},
|
163
|
+
] as any
|
164
|
+
|
165
|
+
const tableRef = ref(null)
|
166
|
+
const pagination = ref({ rowsPerPage: 10 })
|
167
|
+
|
168
|
+
// Функция для переключения раскрытия строки
|
169
|
+
const toggleExpand = (id: string) => {
|
170
|
+
expanded.set(id, !expanded.get(id))
|
171
|
+
if (!currentTab[id]) {
|
172
|
+
currentTab[id] = 'info' // Устанавливаем вкладку по умолчанию
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
const getName = (account: IAccount) => {
|
177
|
+
if (account?.private_account?.type === AccountTypes.Individual){
|
178
|
+
return `${account.private_account.individual_data?.last_name} ${account.private_account.individual_data?.first_name} ${account.private_account.individual_data?.middle_name}`
|
179
|
+
} else if (account?.private_account?.type === AccountTypes.Entrepreneur){
|
180
|
+
return `${account.private_account.entrepreneur_data?.last_name} ${account.private_account.entrepreneur_data?.first_name} ${account.private_account.entrepreneur_data?.middle_name}`
|
181
|
+
} else if (account?.private_account?.type === AccountTypes.Organization){
|
182
|
+
return `${account.private_account.organization_data?.short_name}`
|
183
|
+
}
|
184
|
+
}
|
102
185
|
|
103
|
-
</script>
|
186
|
+
</script>
|
@@ -142,29 +142,29 @@ div
|
|
142
142
|
)
|
143
143
|
</template>
|
144
144
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
145
|
+
<script lang="ts" setup>
|
146
|
+
import { useEditableData } from 'src/shared/lib/composables/useEditableData';
|
147
|
+
import { notEmpty, notEmptyPhone } from 'src/shared/lib/utils';
|
148
|
+
import { validEmail } from 'src/shared/lib/utils/validEmailRule';
|
149
|
+
import { validatePersonalName } from 'src/shared/lib/utils';
|
150
|
+
import { EditableActions } from 'src/shared/ui/EditableActions';
|
151
|
+
import { Cooperative } from 'cooptypes';
|
152
152
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
153
|
+
const props = defineProps({
|
154
|
+
individual: {
|
155
|
+
type: Object as () => Cooperative.Users.IIndividualData,
|
156
|
+
required: true
|
157
|
+
},
|
158
|
+
readonly: {
|
159
|
+
type: Boolean,
|
160
|
+
required: false,
|
161
|
+
default: true
|
162
|
+
}
|
163
|
+
});
|
164
164
|
|
165
|
-
|
166
|
-
|
167
|
-
|
165
|
+
const handleSave = (updatedIndividual: Cooperative.Users.IIndividualData) => {
|
166
|
+
console.log('Сохранено:', updatedIndividual);
|
167
|
+
};
|
168
168
|
|
169
|
-
|
170
|
-
|
169
|
+
const { editableData: data, isEditing, saveChanges, cancelChanges } = useEditableData(props.individual, handleSave);
|
170
|
+
</script>
|