Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5639f8031c | ||
|
|
d42e21bae7 |
7 changed files with 178 additions and 70 deletions
|
|
@ -15,13 +15,26 @@ export const useZeroPrices = () => {
|
|||
await apiClient.delete('/merch/zeroprices', list)
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Delete zero prices error: ', error)
|
||||
console.log('Delete target zero prices error: ', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const deleteZeroPricesPeriod = async (start, end) => {
|
||||
const params = new URLSearchParams({ start, end }).toString()
|
||||
const url = `/merch/zeroprices/period${params ? `?${params}` : ''}`
|
||||
const response = await apiClient.delete(url)
|
||||
|
||||
if (response.status === 200) {
|
||||
return response
|
||||
} else {
|
||||
console.log('Delete period select zero prices error: ', response)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getZeroPrices,
|
||||
deleteZeroPrices,
|
||||
deleteZeroPricesPeriod,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from 'chart.js'
|
||||
import { Line } from 'vue-chartjs'
|
||||
import 'chartjs-adapter-date-fns'
|
||||
import { originColors } from '@/services/colors.js'
|
||||
|
||||
ChartJS.register(
|
||||
Title,
|
||||
|
|
@ -33,11 +34,6 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
const originColors = {
|
||||
surugaya: '#2d3081',
|
||||
mandarake: '#924646',
|
||||
}
|
||||
|
||||
const chartData = ref({
|
||||
datasets: [],
|
||||
})
|
||||
|
|
|
|||
4
src/services/colors.js
Normal file
4
src/services/colors.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const originColors = {
|
||||
surugaya: '#2d3081',
|
||||
mandarake: '#924646',
|
||||
};
|
||||
|
|
@ -1,69 +1,20 @@
|
|||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useZeroPrices } from '@/api/zeroPrices.js'
|
||||
import ZeroPriceCard from '@/views/ZeroPricesView/ZeroPriceCard.vue'
|
||||
import ZeroPricesToolbar from '@/views/ZeroPricesView/ZeroPricesToolbar.vue'
|
||||
|
||||
const { getZeroPrices } = useZeroPrices()
|
||||
|
||||
const zeroPrices = ref([])
|
||||
const toDelete = ref([])
|
||||
|
||||
const handleToggle = ({ id, merch_uuid, checked }) => {
|
||||
if (checked) {
|
||||
toDelete.value.push({ id, merch_uuid });
|
||||
} else {
|
||||
toDelete.value = toDelete.value.filter(item => item.id !== id);
|
||||
}
|
||||
}
|
||||
|
||||
const handleSelectAll = () => {
|
||||
toDelete.value = zeroPrices.value.map(item => ({
|
||||
id: item.id,
|
||||
merch_uuid: item.merch_uuid
|
||||
}))
|
||||
}
|
||||
|
||||
const fetchZeroPrices = async () => {
|
||||
try {
|
||||
const response = await getZeroPrices()
|
||||
zeroPrices.value = Array.isArray(response.data) ? response.data : []
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleted = () => {
|
||||
toDelete.value = []
|
||||
fetchZeroPrices()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchZeroPrices()
|
||||
})
|
||||
import ScrollToTopButton from '@/components/ScrollToTopButton.vue'
|
||||
import TargetZeroesTab from '@/views/ZeroPricesView/TargetZeroesTab.vue'
|
||||
import PeriodSelectTab from '@/views/ZeroPricesView/PeriodSelectTab.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="zeroPrices.length === 0">
|
||||
<n-h2 class="text-center">Zero prices</n-h2>
|
||||
<n-h3 class="text-center">No data</n-h3>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="sticky-search-container">
|
||||
<ZeroPricesToolbar
|
||||
:selected="toDelete"
|
||||
@deleted="handleDeleted"
|
||||
@selectAll="handleSelectAll"
|
||||
/>
|
||||
</div>
|
||||
<div v-for="item in zeroPrices" :key="item.created_at">
|
||||
<ZeroPriceCard
|
||||
:zero-price="item"
|
||||
@toggle="handleToggle"
|
||||
:checked="toDelete.some(t => t.id === item.id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<n-tabs type="line" animated>
|
||||
<n-tab-pane name="Target zeroes" tab="Target zeroes">
|
||||
<TargetZeroesTab />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="Period select" tab="Period select">
|
||||
<PeriodSelectTab />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<ScrollToTopButton />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
</style>
|
||||
|
|
|
|||
54
src/views/ZeroPricesView/PeriodSelectTab.vue
Normal file
54
src/views/ZeroPricesView/PeriodSelectTab.vue
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useZeroPrices } from '@/api/zeroPrices.js'
|
||||
|
||||
const range = ref(null)
|
||||
|
||||
const setTodayRange = () => {
|
||||
const now = new Date()
|
||||
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
const end = new Date(start.getTime() + 24 * 60 * 60 * 1000)
|
||||
|
||||
range.value = [start.getTime(), end.getTime()]
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setTodayRange()
|
||||
})
|
||||
|
||||
const toRFCtime = (timestamp) => {
|
||||
return new Date(timestamp).toISOString()
|
||||
}
|
||||
|
||||
const { deleteZeroPricesPeriod } = useZeroPrices()
|
||||
|
||||
const deleteEnabled = computed(() => {
|
||||
return range.value === null
|
||||
})
|
||||
|
||||
const deletePeriod = async () => {
|
||||
if (range.value !== null) {
|
||||
const start = toRFCtime(range.value[0])
|
||||
const end = toRFCtime(range.value[1])
|
||||
await deleteZeroPricesPeriod(start, end)
|
||||
} else {
|
||||
console.log('Delete period select zero prices error')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-date-picker
|
||||
v-model:value="range"
|
||||
type="datetimerange"
|
||||
format="HH:mm:ss dd-MM-yyyy"
|
||||
clearable
|
||||
/>
|
||||
<div class="button-container-center">
|
||||
<n-button class="center-button w360" type="error" :disabled="deleteEnabled" @click="deletePeriod">Delete</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
71
src/views/ZeroPricesView/TargetZeroesTab.vue
Normal file
71
src/views/ZeroPricesView/TargetZeroesTab.vue
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useZeroPrices } from '@/api/zeroPrices.js'
|
||||
import ZeroPriceCard from '@/views/ZeroPricesView/ZeroPriceCard.vue'
|
||||
import ZeroPricesToolbar from '@/views/ZeroPricesView/ZeroPricesToolbar.vue'
|
||||
|
||||
const { getZeroPrices } = useZeroPrices()
|
||||
|
||||
const zeroPrices = ref([])
|
||||
const toDelete = ref([])
|
||||
|
||||
const handleToggle = ({ id, merch_uuid, checked }) => {
|
||||
if (checked) {
|
||||
toDelete.value.push({ id, merch_uuid });
|
||||
} else {
|
||||
toDelete.value = toDelete.value.filter(item => item.id !== id);
|
||||
}
|
||||
}
|
||||
|
||||
const handleSelectAll = () => {
|
||||
toDelete.value = zeroPrices.value.map(item => ({
|
||||
id: item.id,
|
||||
merch_uuid: item.merch_uuid
|
||||
}))
|
||||
}
|
||||
|
||||
const fetchZeroPrices = async () => {
|
||||
try {
|
||||
const response = await getZeroPrices()
|
||||
zeroPrices.value = Array.isArray(response.data) ? response.data : []
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleted = () => {
|
||||
toDelete.value = []
|
||||
fetchZeroPrices()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchZeroPrices()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="zeroPrices.length === 0">
|
||||
<n-h2 class="text-center">Zero prices</n-h2>
|
||||
<n-h3 class="text-center">No data</n-h3>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="sticky-search-container">
|
||||
<ZeroPricesToolbar
|
||||
:selected="toDelete"
|
||||
@deleted="handleDeleted"
|
||||
@selectAll="handleSelectAll"
|
||||
/>
|
||||
</div>
|
||||
<div v-for="item in zeroPrices" :key="item.created_at">
|
||||
<ZeroPriceCard
|
||||
:zero-price="item"
|
||||
@toggle="handleToggle"
|
||||
:checked="toDelete.some(t => t.id === item.id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { originColors } from '@/services/colors.js'
|
||||
|
||||
const props = defineProps({
|
||||
zeroPrice: {
|
||||
type: Object,
|
||||
|
|
@ -19,6 +22,10 @@ const handleCheckboxChange = (newValue) => {
|
|||
checked: newValue,
|
||||
})
|
||||
}
|
||||
|
||||
const currentOriginColor = computed(() => {
|
||||
return originColors[props.zeroPrice.origin] || '#fff';
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -31,7 +38,12 @@ const handleCheckboxChange = (newValue) => {
|
|||
</n-gi>
|
||||
<n-gi><strong>Name:</strong> {{ props.zeroPrice.name }}</n-gi>
|
||||
<n-gi><strong>Created:</strong> {{ props.zeroPrice.created_at }}</n-gi>
|
||||
<n-gi><strong>Origin:</strong> {{ props.zeroPrice.origin }}</n-gi>
|
||||
<n-gi
|
||||
><strong>Origin:</strong>
|
||||
<span class="bordered" :style="{ borderColor: currentOriginColor }">
|
||||
{{ props.zeroPrice.origin }}
|
||||
</span>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -49,4 +61,11 @@ const handleCheckboxChange = (newValue) => {
|
|||
gap: 12px;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.bordered {
|
||||
border: 1px solid;
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
margin: 3px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue