FRONT правки
Правки фронта
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
// Базовый URL API (берётся из внешнего config.js)
|
// Базовый URL API (берётся из внешнего config.js)
|
||||||
const API_BASE = window.APP_CONFIG?.API_BASE || 'http://localhost'
|
const API_BASE = window.APP_CONFIG?.API_BASE || ''
|
||||||
|
|
||||||
|
// Формирование полного URL (добавляет домен к относительным путям)
|
||||||
|
export const getFullUrl = (url) => {
|
||||||
|
if (!url) return ''
|
||||||
|
if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('data:')) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
return API_BASE + url
|
||||||
|
}
|
||||||
|
|
||||||
// Базовая функция запроса
|
// Базовая функция запроса
|
||||||
const request = async (endpoint, options = {}) => {
|
const request = async (endpoint, options = {}) => {
|
||||||
|
|||||||
@@ -110,12 +110,12 @@ const filteredTotalTasks = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const inProgressTasks = computed(() => {
|
const inProgressTasks = computed(() => {
|
||||||
const col = filteredColumns.value.find(c => c.id === 3) // В работе
|
const col = filteredColumns.value.find(c => c.id === 2) // В работе
|
||||||
return col ? col.cards.length : 0
|
return col ? col.cards.length : 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const completedTasks = computed(() => {
|
const completedTasks = computed(() => {
|
||||||
const col = filteredColumns.value.find(c => c.id === 5) // Готово
|
const col = filteredColumns.value.find(c => c.id === 4) // Готово
|
||||||
return col ? col.cards.length : 0
|
return col ? col.cards.length : 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<div v-if="card.assignee" class="assignee">
|
<div v-if="card.assignee" class="assignee">
|
||||||
<img
|
<img
|
||||||
v-if="isAvatarUrl(card.assignee)"
|
v-if="isAvatarUrl(card.assignee)"
|
||||||
:src="card.assignee"
|
:src="getFullUrl(card.assignee)"
|
||||||
alt="avatar"
|
alt="avatar"
|
||||||
class="assignee-img"
|
class="assignee-img"
|
||||||
/>
|
/>
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUpdated } from 'vue'
|
import { ref, computed, onMounted, onUpdated } from 'vue'
|
||||||
|
import { getFullUrl } from '../api'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
card: Object,
|
card: Object,
|
||||||
@@ -143,7 +144,7 @@ const daysLeftText = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isAvatarUrl = (value) => {
|
const isAvatarUrl = (value) => {
|
||||||
return value && (value.startsWith('http://') || value.startsWith('https://'))
|
return value && (value.startsWith('http://') || value.startsWith('https://') || value.startsWith('/'))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
<div class="user-dropdown" v-if="!usersLoading" ref="dropdownRef">
|
<div class="user-dropdown" v-if="!usersLoading" ref="dropdownRef">
|
||||||
<!-- Выбранный пользователь / триггер -->
|
<!-- Выбранный пользователь / триггер -->
|
||||||
<button class="dropdown-trigger" @click="toggleDropdown">
|
<button class="dropdown-trigger" @click="toggleDropdown">
|
||||||
<img v-if="selectedUser" :src="selectedUser.avatar_url" :alt="selectedUser.name" class="user-avatar">
|
<img v-if="selectedUser" :src="getFullUrl(selectedUser.avatar_url)" :alt="selectedUser.name" class="user-avatar">
|
||||||
<span v-else class="no-user-icon">—</span>
|
<span v-else class="no-user-icon">—</span>
|
||||||
<span class="user-name">{{ selectedUser ? selectedUser.name : 'Без исполнителя' }}</span>
|
<span class="user-name">{{ selectedUser ? selectedUser.name : 'Без исполнителя' }}</span>
|
||||||
<i data-lucide="chevron-down" class="dropdown-arrow"></i>
|
<i data-lucide="chevron-down" class="dropdown-arrow"></i>
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
:class="{ active: form.userId === user.id }"
|
:class="{ active: form.userId === user.id }"
|
||||||
@click="selectUser(user.id)"
|
@click="selectUser(user.id)"
|
||||||
>
|
>
|
||||||
<img :src="user.avatar_url" :alt="user.name" class="user-avatar">
|
<img :src="getFullUrl(user.avatar_url)" :alt="user.name" class="user-avatar">
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<span class="user-name">{{ user.name }}</span>
|
<span class="user-name">{{ user.name }}</span>
|
||||||
<span class="user-telegram">{{ user.telegram }}</span>
|
<span class="user-telegram">{{ user.telegram }}</span>
|
||||||
@@ -290,7 +290,7 @@
|
|||||||
import { ref, reactive, computed, watch, onMounted, onUpdated, onUnmounted, nextTick } from 'vue'
|
import { ref, reactive, computed, watch, onMounted, onUpdated, onUnmounted, nextTick } from 'vue'
|
||||||
import DatePicker from './DatePicker.vue'
|
import DatePicker from './DatePicker.vue'
|
||||||
import ConfirmDialog from './ConfirmDialog.vue'
|
import ConfirmDialog from './ConfirmDialog.vue'
|
||||||
import { usersApi, taskImageApi } from '../api'
|
import { usersApi, taskImageApi, getFullUrl } from '../api'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: Boolean,
|
show: Boolean,
|
||||||
@@ -337,16 +337,6 @@ const isDragging = ref(false)
|
|||||||
const fileError = ref('')
|
const fileError = ref('')
|
||||||
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg']
|
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg']
|
||||||
const maxFileSize = 10 * 1024 * 1024 // 10 MB
|
const maxFileSize = 10 * 1024 * 1024 // 10 MB
|
||||||
const API_BASE = window.APP_CONFIG?.API_BASE || ''
|
|
||||||
|
|
||||||
// Формирование полного URL (добавляет домен к относительным путям)
|
|
||||||
const getFullUrl = (url) => {
|
|
||||||
if (!url) return ''
|
|
||||||
if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('data:')) {
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
return API_BASE + url
|
|
||||||
}
|
|
||||||
|
|
||||||
// Видимые файлы (без помеченных на удаление)
|
// Видимые файлы (без помеченных на удаление)
|
||||||
const visibleFiles = computed(() => attachedFiles.value.filter(f => !f.toDelete))
|
const visibleFiles = computed(() => attachedFiles.value.filter(f => !f.toDelete))
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
class="team-card"
|
class="team-card"
|
||||||
>
|
>
|
||||||
<div class="card-avatar">
|
<div class="card-avatar">
|
||||||
<img :src="user.avatar_url" :alt="user.name">
|
<img :src="getFullUrl(user.avatar_url)" :alt="user.name">
|
||||||
</div>
|
</div>
|
||||||
<div class="card-info">
|
<div class="card-info">
|
||||||
<h3 class="card-name">{{ user.name }}</h3>
|
<h3 class="card-name">{{ user.name }}</h3>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
import { ref, onMounted, onUpdated } from 'vue'
|
import { ref, onMounted, onUpdated } from 'vue'
|
||||||
import Sidebar from '../components/Sidebar.vue'
|
import Sidebar from '../components/Sidebar.vue'
|
||||||
import Header from '../components/Header.vue'
|
import Header from '../components/Header.vue'
|
||||||
import { usersApi } from '../api'
|
import { usersApi, getFullUrl } from '../api'
|
||||||
|
|
||||||
const users = ref([])
|
const users = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
|
|||||||
Reference in New Issue
Block a user