🔧 Исправление отображения данных DLE из блокчейна
✅ Основные изменения: - Исправлен дублирование /api в URL запросов к бэкенду - Добавлен новый API endpoint /api/blockchain/read-dle-info для чтения данных из блокчейна - Исправлено отображение количества участников (participantCount вместо initialPartners.length) - Обновлен ManagementView.vue для чтения данных из блокчейна вместо JSON файлов - Добавлены утилиты для чтения данных DLE из блокчейна - Исправлены координаты в форме деплоя (сохранение в localStorage) - Добавлен индикатор прогресса деплоя с редиректом на /management 🔧 Технические детали: - Создан backend/routes/blockchain.js с endpoint для чтения DLE данных - Обновлен backend/app.js для регистрации нового маршрута - Исправлен импорт axios в ManagementView.vue (используется настроенный экземпляр api) - Добавлены скрипты utils/read-dle-info.js и utils/get-rpc-url.js - Обновлен скрипт деплоя для сохранения всех данных в блокчейн 🎯 Результат: - Данные DLE теперь читаются напрямую из блокчейна - Правильное отображение координат и количества участников - Устранены ошибки 404 при запросах к API
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,14 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="dle-multisig-management">
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="dle-multisig-management">
|
||||
<div class="multisig-header">
|
||||
<h3>🔐 Управление мультиподписью</h3>
|
||||
<button class="btn btn-primary" @click="showCreateForm = true">
|
||||
@@ -272,17 +279,25 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ref, computed, onMounted, defineProps, defineEmits } from 'vue';
|
||||
import { useAuthContext } from '@/composables/useAuth';
|
||||
import BaseLayout from '../../components/BaseLayout.vue';
|
||||
|
||||
const props = defineProps({
|
||||
dleAddress: { type: String, required: true },
|
||||
dleContract: { type: Object, required: true }
|
||||
dleAddress: { type: String, required: false, default: null },
|
||||
dleContract: { type: Object, required: false, default: null },
|
||||
isAuthenticated: Boolean,
|
||||
identities: Array,
|
||||
tokenBalances: Object,
|
||||
isLoadingTokens: Boolean
|
||||
});
|
||||
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const { address } = useAuthContext();
|
||||
|
||||
// Состояние формы
|
||||
|
||||
@@ -11,10 +11,29 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="dle-proposals-management">
|
||||
<BaseLayout
|
||||
:is-authenticated="isAuthenticated"
|
||||
:identities="identities"
|
||||
:token-balances="tokenBalances"
|
||||
:is-loading-tokens="isLoadingTokens"
|
||||
@auth-action-completed="$emit('auth-action-completed')"
|
||||
>
|
||||
<div class="dle-proposals-management">
|
||||
<div class="proposals-header">
|
||||
<h3>🗳️ Управление предложениями</h3>
|
||||
<button class="btn btn-primary" @click="showCreateForm = true">
|
||||
<div class="header-info">
|
||||
<h3>🗳️ Управление предложениями</h3>
|
||||
<div v-if="selectedDle" class="dle-info">
|
||||
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
|
||||
<span class="dle-address">{{ shortenAddress(selectedDle.dleAddress) }}</span>
|
||||
</div>
|
||||
<div v-else-if="isLoadingDle" class="loading-info">
|
||||
<span>Загрузка данных DLE...</span>
|
||||
</div>
|
||||
<div v-else class="no-dle-info">
|
||||
<span>DLE не выбран</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="showCreateForm = true" :disabled="!selectedDle">
|
||||
<i class="fas fa-plus"></i> Создать предложение
|
||||
</button>
|
||||
</div>
|
||||
@@ -322,18 +341,41 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ref, computed, onMounted, watch, defineProps, defineEmits } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAuthContext } from '@/composables/useAuth';
|
||||
import BaseLayout from '../../components/BaseLayout.vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const props = defineProps({
|
||||
dleAddress: { type: String, required: true },
|
||||
dleContract: { type: Object, required: true }
|
||||
dleAddress: { type: String, required: false, default: null },
|
||||
dleContract: { type: Object, required: false, default: null },
|
||||
isAuthenticated: Boolean,
|
||||
identities: Array,
|
||||
tokenBalances: Object,
|
||||
isLoadingTokens: Boolean
|
||||
});
|
||||
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const { address } = useAuthContext();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// Получаем адрес DLE из URL
|
||||
const dleAddress = computed(() => {
|
||||
const address = route.query.address || props.dleAddress;
|
||||
console.log('DLE Address from URL:', address);
|
||||
return address;
|
||||
});
|
||||
|
||||
// Состояние DLE
|
||||
const selectedDle = ref(null);
|
||||
const isLoadingDle = ref(false);
|
||||
|
||||
// Состояние формы
|
||||
const showCreateForm = ref(false);
|
||||
@@ -382,6 +424,38 @@ const filteredProposals = computed(() => {
|
||||
});
|
||||
|
||||
// Функции
|
||||
async function loadDleData() {
|
||||
console.log('loadDleData вызвана с адресом:', dleAddress.value);
|
||||
|
||||
if (!dleAddress.value) {
|
||||
console.warn('Адрес DLE не указан');
|
||||
return;
|
||||
}
|
||||
|
||||
isLoadingDle.value = true;
|
||||
try {
|
||||
// Загружаем данные DLE из backend
|
||||
const response = await axios.get(`/dle-v2`);
|
||||
const dles = response.data.data; // Используем response.data.data
|
||||
console.log('Получены DLE из API:', dles);
|
||||
|
||||
// Находим нужный DLE по адресу
|
||||
const dle = dles.find(d => d.dleAddress === dleAddress.value);
|
||||
console.log('Найденный DLE:', dle);
|
||||
|
||||
if (dle) {
|
||||
selectedDle.value = dle;
|
||||
console.log('Загружен DLE:', dle);
|
||||
} else {
|
||||
console.warn('DLE не найден:', dleAddress.value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки DLE:', error);
|
||||
} finally {
|
||||
isLoadingDle.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function validateOperationParams() {
|
||||
const params = newProposal.value.operationParams;
|
||||
|
||||
@@ -606,7 +680,15 @@ function viewProposalDetails(proposalId) {
|
||||
// console.log('Просмотр деталей предложения:', proposalId);
|
||||
}
|
||||
|
||||
// Отслеживаем изменения в адресе DLE
|
||||
watch(dleAddress, (newAddress) => {
|
||||
if (newAddress) {
|
||||
loadDleData();
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
onMounted(() => {
|
||||
// Загрузка предложений
|
||||
loadProposals();
|
||||
});
|
||||
</script>
|
||||
@@ -623,6 +705,47 @@ onMounted(() => {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.header-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.header-info h3 {
|
||||
margin: 0;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.dle-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
font-family: monospace;
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
background: #f8f9fa;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.loading-info,
|
||||
.no-dle-info {
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.create-proposal-form {
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -23,7 +23,16 @@
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1>Токены DLE</h1>
|
||||
<p>Балансы, трансферы и распределение токенов</p>
|
||||
<div v-if="selectedDle" class="dle-info">
|
||||
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
|
||||
<span class="dle-address">{{ shortenAddress(selectedDle.dleAddress) }}</span>
|
||||
</div>
|
||||
<div v-else-if="isLoadingDle" class="loading-info">
|
||||
<span>Загрузка данных DLE...</span>
|
||||
</div>
|
||||
<div v-else class="no-dle-info">
|
||||
<span>DLE не выбран</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="close-btn" @click="router.push('/management')">×</button>
|
||||
</div>
|
||||
@@ -185,9 +194,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineProps, defineEmits } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ref, computed, onMounted, watch, defineProps, defineEmits } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import BaseLayout from '../../components/BaseLayout.vue';
|
||||
import axios from 'axios';
|
||||
|
||||
// Определяем props
|
||||
const props = defineProps({
|
||||
@@ -201,16 +211,28 @@ const props = defineProps({
|
||||
const emit = defineEmits(['auth-action-completed']);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// Получаем адрес DLE из URL
|
||||
const dleAddress = computed(() => {
|
||||
const address = route.query.address;
|
||||
console.log('DLE Address from URL (Tokens):', address);
|
||||
return address;
|
||||
});
|
||||
|
||||
// Состояние DLE
|
||||
const selectedDle = ref(null);
|
||||
const isLoadingDle = ref(false);
|
||||
|
||||
// Состояние
|
||||
const isTransferring = ref(false);
|
||||
const isDistributing = ref(false);
|
||||
|
||||
// Данные токенов
|
||||
const tokenSymbol = ref('MDLE');
|
||||
const totalSupply = ref(10000);
|
||||
const userBalance = ref(1000);
|
||||
const quorumPercentage = ref(51);
|
||||
// Данные токенов (реактивные)
|
||||
const tokenSymbol = computed(() => selectedDle.value?.symbol || 'MDLE');
|
||||
const totalSupply = computed(() => selectedDle.value?.initialAmounts?.[0] || 10000);
|
||||
const userBalance = computed(() => Math.floor(totalSupply.value * 0.1)); // 10% для демо
|
||||
const quorumPercentage = computed(() => selectedDle.value?.governanceSettings?.quorumPercentage || 51);
|
||||
const tokenPrice = ref(1.25);
|
||||
|
||||
// Данные трансфера
|
||||
@@ -237,6 +259,41 @@ const tokenHolders = ref([
|
||||
{ address: '0x5678901234567890123456789012345678901234', balance: 600 }
|
||||
]);
|
||||
|
||||
// Функции
|
||||
async function loadDleData() {
|
||||
if (!dleAddress.value) {
|
||||
console.warn('Адрес DLE не указан');
|
||||
return;
|
||||
}
|
||||
|
||||
isLoadingDle.value = true;
|
||||
try {
|
||||
// Загружаем данные DLE из backend
|
||||
const response = await axios.get(`/dle-v2`);
|
||||
const dles = response.data.data; // Используем response.data.data
|
||||
|
||||
// Находим нужный DLE по адресу
|
||||
const dle = dles.find(d => d.dleAddress === dleAddress.value);
|
||||
|
||||
if (dle) {
|
||||
selectedDle.value = dle;
|
||||
console.log('Загружен DLE:', dle);
|
||||
console.log('Данные токенов будут обновлены автоматически');
|
||||
} else {
|
||||
console.warn('DLE не найден:', dleAddress.value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки DLE:', error);
|
||||
} finally {
|
||||
isLoadingDle.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function shortenAddress(address) {
|
||||
if (!address) return '';
|
||||
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
||||
}
|
||||
|
||||
// Методы
|
||||
const transferTokens = async () => {
|
||||
if (isTransferring.value) return;
|
||||
@@ -324,6 +381,13 @@ const formatAddress = (address) => {
|
||||
if (!address) return '';
|
||||
return address.substring(0, 6) + '...' + address.substring(address.length - 4);
|
||||
};
|
||||
|
||||
// Отслеживаем изменения в адресе DLE
|
||||
watch(dleAddress, (newAddress) => {
|
||||
if (newAddress) {
|
||||
loadDleData();
|
||||
}
|
||||
}, { immediate: true });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -361,6 +425,38 @@ const formatAddress = (address) => {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dle-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.dle-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.dle-address {
|
||||
font-family: monospace;
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
background: #f8f9fa;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.loading-info,
|
||||
.no-dle-info {
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
Reference in New Issue
Block a user