first
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { paramsApi } from "@/api.ts";
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
const res = ref({});
|
||||
onMounted(async () => {
|
||||
res.value = await paramsApi.list();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>You did it!</h1>
|
||||
<p>
|
||||
Visit {{ res }}
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">vuejs.org</a> to
|
||||
read the documentation
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createModelApi } from "@/api_client.ts";
|
||||
import type { Params, ControlCase } from "@/models.ts";
|
||||
|
||||
export const paramsApi = createModelApi<Params>("params");
|
||||
export const controlCaseApi = createModelApi<ControlCase>("control-case");
|
||||
@@ -0,0 +1,176 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface BaseEntity {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface ListParams {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
search?: string;
|
||||
ordering?: string;
|
||||
[key: string]: string | number | boolean | undefined;
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
count: number;
|
||||
next: string | null;
|
||||
previous: string | null;
|
||||
results: T[];
|
||||
}
|
||||
|
||||
export function isPaginatedResponse<T>(
|
||||
payload: PaginatedResponse<T> | T[],
|
||||
): payload is PaginatedResponse<T> {
|
||||
return !Array.isArray(payload);
|
||||
}
|
||||
|
||||
export function unwrapListResponse<T>(
|
||||
payload: PaginatedResponse<T> | T[],
|
||||
): T[] {
|
||||
return Array.isArray(payload) ? payload : payload.results;
|
||||
}
|
||||
|
||||
export interface ModelApi<
|
||||
T extends BaseEntity,
|
||||
CreateDTO = Partial<T>,
|
||||
UpdateDTO = Partial<T>,
|
||||
> {
|
||||
list: (params?: ListParams) => Promise<PaginatedResponse<T> | T[]>;
|
||||
listAll: (params?: ListParams) => Promise<T[]>;
|
||||
retrieve: (id: number) => Promise<T>;
|
||||
create: (payload: CreateDTO) => Promise<T>;
|
||||
update: (id: number, payload: UpdateDTO) => Promise<T>;
|
||||
partialUpdate: (id: number, payload: Partial<UpdateDTO>) => Promise<T>;
|
||||
remove: (id: number) => Promise<void>;
|
||||
}
|
||||
|
||||
export const apiClient = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL ?? "/api",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
function stringifyApiErrorValue(value: unknown): string {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(stringifyApiErrorValue).filter(Boolean).join(", ");
|
||||
}
|
||||
|
||||
if (value && typeof value === "object") {
|
||||
return Object.entries(value)
|
||||
.map(
|
||||
([key, nestedValue]) =>
|
||||
`${key}: ${stringifyApiErrorValue(nestedValue)}`,
|
||||
)
|
||||
.filter(Boolean)
|
||||
.join("; ");
|
||||
}
|
||||
|
||||
return value == null ? "" : String(value);
|
||||
}
|
||||
|
||||
export function formatApiError(
|
||||
error: unknown,
|
||||
fallback = "Не удалось выполнить запрос.",
|
||||
): string {
|
||||
if (!axios.isAxiosError(error)) return fallback;
|
||||
|
||||
const payload = error.response?.data;
|
||||
if (!payload) return error.message || fallback;
|
||||
|
||||
if (typeof payload === "string") return payload;
|
||||
|
||||
if (typeof payload === "object") {
|
||||
const message = Object.entries(payload)
|
||||
.map(([key, value]) => {
|
||||
const text = stringifyApiErrorValue(value);
|
||||
if (!text) return "";
|
||||
if (key === "detail" || key === "non_field_errors") return text;
|
||||
return `${key}: ${text}`;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join("; ");
|
||||
|
||||
return message || fallback;
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export function createModelApi<
|
||||
T extends BaseEntity,
|
||||
CreateDTO = Partial<T>,
|
||||
UpdateDTO = Partial<T>,
|
||||
>(resource: string): ModelApi<T, CreateDTO, UpdateDTO> {
|
||||
const normalized = resource.endsWith("/") ? resource : `${resource}/`;
|
||||
|
||||
return {
|
||||
async list(params) {
|
||||
const response = await apiClient.get<PaginatedResponse<T> | T[]>(
|
||||
normalized,
|
||||
{ params },
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async listAll(params) {
|
||||
const firstPage = await apiClient.get<PaginatedResponse<T> | T[]>(
|
||||
normalized,
|
||||
{
|
||||
params: {
|
||||
...(params ?? {}),
|
||||
page: 1,
|
||||
page_size: params?.page_size ?? 100,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const firstPayload = firstPage.data;
|
||||
if (!isPaginatedResponse(firstPayload)) {
|
||||
return firstPayload;
|
||||
}
|
||||
|
||||
const items = [...firstPayload.results];
|
||||
let nextUrl = firstPayload.next;
|
||||
|
||||
while (nextUrl) {
|
||||
const nextPage = await apiClient.get<PaginatedResponse<T>>(nextUrl);
|
||||
items.push(...nextPage.data.results);
|
||||
nextUrl = nextPage.data.next;
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
async retrieve(id) {
|
||||
const response = await apiClient.get<T>(`${normalized}${id}/`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async create(payload) {
|
||||
const response = await apiClient.post<T>(
|
||||
normalized,
|
||||
payload,
|
||||
payload instanceof FormData
|
||||
? { headers: { "Content-Type": "multipart/form-data" } }
|
||||
: undefined,
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async update(id, payload) {
|
||||
const response = await apiClient.put<T>(`${normalized}${id}/`, payload);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async partialUpdate(id, payload) {
|
||||
const response = await apiClient.patch<T>(`${normalized}${id}/`, payload);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async remove(id) {
|
||||
await apiClient.delete(`${normalized}${id}/`);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
@@ -0,0 +1,23 @@
|
||||
export interface Params {
|
||||
id: number;
|
||||
rel: number;
|
||||
relC: number;
|
||||
le: number;
|
||||
pr: number;
|
||||
pe: number;
|
||||
initialCondition: string;
|
||||
time: number;
|
||||
folderPath: string;
|
||||
}
|
||||
|
||||
export interface ControlCase {
|
||||
id: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
|
||||
name: string;
|
||||
paramsId: number;
|
||||
params: Params;
|
||||
status: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [],
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
Reference in New Issue
Block a user