search item

This commit is contained in:
2023-10-18 17:59:30 +02:00
parent 4fca2712bf
commit 9bd1ced9d4
5 changed files with 131 additions and 16 deletions

View File

@@ -20,5 +20,15 @@ export const getMarketTypes = async (types: (string | number)[]): Promise<Market
} else if (types.length === 1 && typeof types[0] === "number") {
return [(await apiAxiosInstance.get<MarketType>(`/sde/types/${types[0]}/`)).data];
}
return (await apiAxiosInstance.post<MarketType[]>("/sde/types/search", types.map(t => [typeof t === "number" ? 'id' : "name", t]))).data;
return (await apiAxiosInstance.post<MarketType[]>("/sde/types/search", types.map(t => {
if (typeof t === "number") {
return { id: t };
} else {
return { name: t };
}
}))).data;
}
export const searchMarketTypes = async (search: string): Promise<MarketType[]> => {
return (await apiAxiosInstance.post<MarketType[]>("/sde/types/search", [{ name_i: search }])).data;
}

View File

@@ -0,0 +1,102 @@
<script setup lang="ts">
import { useFocus, useVirtualList, useVModel } from '@vueuse/core';
import { nextTick, ref, watch, watchEffect } from 'vue';
import { MarketType, searchMarketTypes } from './MarketType';
import MarketTypeLabel from "./MarketTypeLabel.vue";
interface Props {
modelValue?: MarketType;
}
interface Emits {
(e: 'update:modelValue', value?: MarketType): void;
(e: 'submit'): void;
}
const props = defineProps<Props>();
const emit = defineEmits<Emits>();
const value = useVModel(props, 'modelValue', emit);
const input = ref<HTMLInputElement>();
const {focused} = useFocus(input);
const name = ref('');
const suggestions = ref<MarketType[]>([]);
const currentIndex = ref(-1);
const {list, scrollTo, containerProps, wrapperProps} = useVirtualList(suggestions, {
itemHeight: 24,
overscan: 3
});
const moveDown = () => {
if (currentIndex.value < 0 || currentIndex.value >= suggestions.value.length - 1) {
currentIndex.value = 0;
} else if (currentIndex.value < suggestions.value.length - 1) {
currentIndex.value++;
}
scrollTo(currentIndex.value);
}
const moveUp = () => {
if (currentIndex.value <= 0) {
currentIndex.value = suggestions.value.length - 1;
} else if (currentIndex.value > 0) {
currentIndex.value--;
}
scrollTo(currentIndex.value);
}
const select = (type: MarketType) => {
value.value = type;
currentIndex.value = -1;
suggestions.value = [];
}
const submit = async () => {
if (currentIndex.value >= 0 && currentIndex.value < suggestions.value.length) {
const v = suggestions.value[currentIndex.value];
value.value = v;
await nextTick();
} else if (props.modelValue === undefined && suggestions.value.length > 0) {
value.value = suggestions.value[0];
await nextTick();
}
if (value.value === undefined) {
return;
}
emit('submit');
}
watch(() => props.modelValue, async v => {
if (v === undefined) {
name.value = '';
} else {
name.value = v.name;
}
})
watchEffect(async () => {
const search = name.value.split('\t')[0];
if (!focused.value || search.length < 3) {
suggestions.value = [];
} else {
suggestions.value = await searchMarketTypes(search);
}
currentIndex.value = -1;
})
</script>
<template>
<div>
<input ref="input" type="text" class="w-96" v-model="name" @keyup.enter="submit" @keyup.down="moveDown" @keyup.up="moveUp" />
<div v-if="suggestions.length > 1" class="z-10 absolute w-96">
<div v-bind="containerProps" style="height: 300px">
<div v-bind="wrapperProps">
<div v-for="s in list" :key="s.index" class="hover:bg-slate-700" :class="{'bg-slate-500': s.index !== currentIndex, 'bg-emerald-500': s.index === currentIndex}" @click="$emit('update:modelValue', s.data)">
<MarketTypeLabel :id="s.data.id" :name="s.data.name" class="whitespace-nowrap overflow-hidden cursor-pointer" hideCopy @click="select(s.data)" />
</div>
</div>
</div>
</div>
</div>
</template>

View File

@@ -6,11 +6,13 @@ import { ClipboardIcon } from '@heroicons/vue/24/outline';
interface Props {
name?: string;
id?: number;
hideCopy?: boolean;
}
withDefaults(defineProps<Props>(), {
name: "",
id: 0
id: 0,
hideCopy: false
});
</script>
@@ -19,7 +21,7 @@ withDefaults(defineProps<Props>(), {
<img v-if="id" :src="`https://images.evetech.net/types/${id}/icon`" class="inline-block w-5 h-5 me-1" />
<template v-if="name">
{{ name }}
<button class="btn-icon-stroke" @click="copyToClipboard(name)"><ClipboardIcon class="relative top-0.5 !w-4 !h-4" /></button>
<button v-if="!hideCopy" class="btn-icon-stroke" @click="copyToClipboard(name)"><ClipboardIcon class="relative top-0.5 !w-4 !h-4" /></button>
</template>
</div>
</template>

View File

@@ -1,3 +1,4 @@
export * from './MarketType';
export { default as MarketTypeLabel } from './MarketTypeLabel.vue';
export { default as MarketTypeInput } from './MarketTypeInput.vue';

View File

@@ -1,13 +1,14 @@
<script setup lang="ts">
import { MarketType, MarketTypePrice, getHistory, getMarketType, getMarketTypes, jitaId, useApraisalStore } from "@/market";
import { MarketType, MarketTypePrice, getHistory, getMarketTypes, jitaId, useApraisalStore } from "@/market";
import { ScanResult, ScanResultTable, useMarketScanStore } from '@/market/scan';
import { BuyModal } from '@/market/track';
import MarketTypeInput from "@/market/type/MarketTypeInput.vue";
import { ref, watch } from 'vue';
const buyModal = ref<typeof BuyModal>();
const item = ref("");
const item = ref<MarketType>();
const apraisalStore = useApraisalStore();
const markeyScanStore = useMarketScanStore();
@@ -18,7 +19,7 @@ const addOrRelaod = async (type: MarketType) => {
getHistory(jitaId, typeID),
apraisalStore.getPrice(type)
]);
const item = {
const itm = {
type,
history,
buy: price.buy,
@@ -27,20 +28,19 @@ const addOrRelaod = async (type: MarketType) => {
};
if (items.value.some(i => i.type.id === typeID)) {
items.value = items.value.map(i => i.type.id === typeID ? item : i);
items.value = items.value.map(i => i.type.id === typeID ? itm : i);
} else {
items.value = [ ...items.value, item];
items.value = [ ...items.value, itm];
}
}
const addItem = async () => {
const type = await getMarketType(item.value.split('\t')[0]);
item.value = "";
if (!type) {
// TODO: Show error
if (!item.value) {
// TODO error
return;
}
addOrRelaod(type);
addOrRelaod(item.value);
item.value = undefined;
}
const removeItem = (type: MarketType) => {
items.value = items.value.filter(i => i.type.id !== type.id);
@@ -68,9 +68,9 @@ watch(() => markeyScanStore.types, async t => {
<template>
<div class="grid mb-2 mt-4">
<div class="w-auto">
<div class="w-auto flex">
<span>Item: </span>
<input type="text" class="w-96" v-model="item" @keyup.enter="addItem" />
<MarketTypeInput class="ms-2" v-model="item" @submit="addItem"/>
<button class="justify-self-end ms-2" @click="addItem">Add</button>
</div>
</div>