fix some things
This commit is contained in:
@@ -10,6 +10,25 @@ export const getTvProducts = async (): Promise<Product[]> => {
|
|||||||
const response = await useAxios.get<unknown>(`/customer/tv/products`, { timeout: 15000 })
|
const response = await useAxios.get<unknown>(`/customer/tv/products`, { timeout: 15000 })
|
||||||
return Array.isArray(response) ? (response as Product[]) : []
|
return Array.isArray(response) ? (response as Product[]) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TvServerTime = {
|
||||||
|
unix: number
|
||||||
|
iso: string
|
||||||
|
timezone: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTvServerTime = async (): Promise<TvServerTime | null> => {
|
||||||
|
try {
|
||||||
|
const response = await useAxios.get<TvServerTime>(`/customer/tv/time`, { timeout: 10000 })
|
||||||
|
if (response && typeof (response as TvServerTime).unix === 'number') {
|
||||||
|
return response as TvServerTime
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export const getProductById = async ( id:number|null): Promise<any> => {
|
export const getProductById = async ( id:number|null): Promise<any> => {
|
||||||
return await useAxios.get<Product[]>(`/customer/product/list/${id}`, { timeout: 15000 })
|
return await useAxios.get<Product[]>(`/customer/product/list/${id}`, { timeout: 15000 })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type {Wallet} from "@/models/wallet";
|
|||||||
export interface Product {
|
export interface Product {
|
||||||
id: number
|
id: number
|
||||||
img: string | undefined
|
img: string | undefined
|
||||||
|
image?: string | null
|
||||||
title: string
|
title: string
|
||||||
amount: any
|
amount: any
|
||||||
metal_id: any
|
metal_id: any
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export type ProductImageSource = {
|
|||||||
file?: { id?: number | null; src?: string | null; full_src?: string | null; url?: string | null } | null
|
file?: { id?: number | null; src?: string | null; full_src?: string | null; url?: string | null } | null
|
||||||
file_id?: number | string | null
|
file_id?: number | string | null
|
||||||
img?: string | null
|
img?: string | null
|
||||||
|
image?: string | null
|
||||||
group_type?: string | null
|
group_type?: string | null
|
||||||
} | null | undefined
|
} | null | undefined
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ export function getProductFallbackImage(product?: ProductImageSource): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resolveProductImageSrc(product?: ProductImageSource): string {
|
export function resolveProductImageSrc(product?: ProductImageSource): string {
|
||||||
const remoteSrc = product?.file?.full_src || product?.file?.url || product?.file?.src
|
const remoteSrc = product?.file?.full_src || product?.file?.url || product?.file?.src || product?.image || product?.img
|
||||||
if (isUsableImageSrc(remoteSrc)) {
|
if (isUsableImageSrc(remoteSrc)) {
|
||||||
const url = absolutizeImageUrl(remoteSrc!.trim())
|
const url = absolutizeImageUrl(remoteSrc!.trim())
|
||||||
const version = product?.file_id ?? product?.file?.id
|
const version = product?.file_id ?? product?.file?.id
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ export function getTvProductBuyPrice(product?: Product | null): number {
|
|||||||
}
|
}
|
||||||
const unit = tvBuyUnitPrice(product)
|
const unit = tvBuyUnitPrice(product)
|
||||||
if (unit > 0) {
|
if (unit > 0) {
|
||||||
return unit + resolveProductCashFeeBuyRial(product, unit)
|
return unit
|
||||||
}
|
}
|
||||||
return getProductBuyPrice(product)
|
return getProductBuyPrice(product)
|
||||||
}
|
}
|
||||||
@@ -288,7 +288,7 @@ export function getTvProductSellPrice(product?: Product | null): number {
|
|||||||
}
|
}
|
||||||
const unit = tvSellUnitPrice(product)
|
const unit = tvSellUnitPrice(product)
|
||||||
if (unit > 0) {
|
if (unit > 0) {
|
||||||
return unit - resolveProductCashFeeSellRial(product, unit)
|
return unit
|
||||||
}
|
}
|
||||||
return getProductSellPrice(product)
|
return getProductSellPrice(product)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
import { getTvProducts } from '@/api/products'
|
import { getTvProducts, getTvServerTime } from '@/api/products'
|
||||||
import type { Product } from '@/models/products'
|
import type { Product } from '@/models/products'
|
||||||
import { getConfigApp } from '@/utils/config'
|
import { getConfigApp } from '@/utils/config'
|
||||||
import { getTvProductBuyPrice, getTvProductSellPrice, isTvProductPriceInactive } from '@/utils/productPrice'
|
import { getTvProductBuyPrice, getTvProductSellPrice, isTvProductPriceInactive } from '@/utils/productPrice'
|
||||||
@@ -47,6 +47,7 @@ const REFRESH_MS = 15000
|
|||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const products = ref<Product[]>([])
|
const products = ref<Product[]>([])
|
||||||
const now = ref(Date.now())
|
const now = ref(Date.now())
|
||||||
|
let serverOffsetMs = 0
|
||||||
|
|
||||||
let clockTimer: ReturnType<typeof setInterval> | undefined
|
let clockTimer: ReturnType<typeof setInterval> | undefined
|
||||||
let listTimer: ReturnType<typeof setInterval> | undefined
|
let listTimer: ReturnType<typeof setInterval> | undefined
|
||||||
@@ -57,6 +58,7 @@ const shopName = computed(
|
|||||||
|
|
||||||
const clockText = computed(() =>
|
const clockText = computed(() =>
|
||||||
new Date(now.value).toLocaleTimeString('fa-IR', {
|
new Date(now.value).toLocaleTimeString('fa-IR', {
|
||||||
|
timeZone: 'Asia/Tehran',
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
second: '2-digit',
|
second: '2-digit',
|
||||||
@@ -99,10 +101,27 @@ const rows = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function normalizeTvProduct(raw: Product): Product {
|
||||||
|
const image = (raw as Product & { image?: string | null }).image
|
||||||
|
if (image && !raw.img) {
|
||||||
|
return { ...raw, img: image }
|
||||||
|
}
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
|
||||||
|
async function syncServerClock() {
|
||||||
|
const t0 = Date.now()
|
||||||
|
const payload = await getTvServerTime()
|
||||||
|
const t1 = Date.now()
|
||||||
|
if (!payload?.unix) return
|
||||||
|
const rttHalf = (t1 - t0) / 2
|
||||||
|
serverOffsetMs = payload.unix * 1000 + rttHalf - t1
|
||||||
|
}
|
||||||
|
|
||||||
async function loadProducts() {
|
async function loadProducts() {
|
||||||
try {
|
try {
|
||||||
const list = await getTvProducts()
|
const list = await getTvProducts()
|
||||||
products.value = Array.isArray(list) ? list : []
|
products.value = (Array.isArray(list) ? list : []).map(normalizeTvProduct)
|
||||||
} catch {
|
} catch {
|
||||||
if (!products.value.length) {
|
if (!products.value.length) {
|
||||||
products.value = []
|
products.value = []
|
||||||
@@ -112,17 +131,20 @@ async function loadProducts() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
initPriceRealtimeChannel((key) => settingsStore.getSettingByKey(key))
|
initPriceRealtimeChannel((key) => settingsStore.getSettingByKey(key))
|
||||||
startLastPricePolling()
|
startLastPricePolling()
|
||||||
void getLastPrices()
|
void getLastPrices()
|
||||||
|
await syncServerClock()
|
||||||
|
now.value = Date.now() + serverOffsetMs
|
||||||
void loadProducts()
|
void loadProducts()
|
||||||
clockTimer = setInterval(() => {
|
clockTimer = setInterval(() => {
|
||||||
now.value = Date.now()
|
now.value = Date.now() + serverOffsetMs
|
||||||
}, 1000)
|
}, 1000)
|
||||||
listTimer = setInterval(() => {
|
listTimer = setInterval(() => {
|
||||||
void loadProducts()
|
void loadProducts()
|
||||||
|
void syncServerClock()
|
||||||
}, REFRESH_MS)
|
}, REFRESH_MS)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import { useFullscreen } from '@vueuse/core'
|
import { useFullscreen } from '@vueuse/core'
|
||||||
import type { Product } from '@/models/products'
|
import type { Product } from '@/models/products'
|
||||||
import { getTvProducts } from '@/api/products'
|
import { getTvProducts, getTvServerTime } from '@/api/products'
|
||||||
import { getConfigApp } from '@/utils/config'
|
import { getConfigApp } from '@/utils/config'
|
||||||
import { useSettingsStore } from '@/store/settingsStore'
|
import { useSettingsStore } from '@/store/settingsStore'
|
||||||
import { getTvProductBuyPrice, getTvProductSellPrice, isTvProductPriceInactive } from '@/utils/productPrice'
|
import { getTvProductBuyPrice, getTvProductSellPrice, isTvProductPriceInactive } from '@/utils/productPrice'
|
||||||
@@ -73,7 +73,10 @@ function isBankCoin(product: Product) {
|
|||||||
const title = (product.title ?? '').trim()
|
const title = (product.title ?? '').trim()
|
||||||
if (group === 'سکه' || group === 'سکه بانکی') return true
|
if (group === 'سکه' || group === 'سکه بانکی') return true
|
||||||
if (group.includes('سکه') && !group.includes('پارسیان')) return true
|
if (group.includes('سکه') && !group.includes('پارسیان')) return true
|
||||||
return title.includes('بانکی')
|
if (title.includes('بانکی')) return true
|
||||||
|
// API سبک TV ممکن است group_type نداشته باشد
|
||||||
|
if (title.includes('سکه') && !title.includes('پارسیان')) return true
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function bankCoinRank(product: Product) {
|
function bankCoinRank(product: Product) {
|
||||||
@@ -116,6 +119,8 @@ export function useTvMonitorPage() {
|
|||||||
let clockTimer: ReturnType<typeof setInterval> | undefined
|
let clockTimer: ReturnType<typeof setInterval> | undefined
|
||||||
let cursorTimer: ReturnType<typeof setTimeout> | undefined
|
let cursorTimer: ReturnType<typeof setTimeout> | undefined
|
||||||
let listTimer: ReturnType<typeof setInterval> | undefined
|
let listTimer: ReturnType<typeof setInterval> | undefined
|
||||||
|
/** اختلاف میلیثانیهٔ ساعت سرور (Asia/Tehran) با Date.now محلی */
|
||||||
|
let serverOffsetMs = 0
|
||||||
|
|
||||||
const brandName = computed(
|
const brandName = computed(
|
||||||
() => String(getConfigApp('name_fa') || getConfigApp('name') || 'تابلو قیمت'),
|
() => String(getConfigApp('name_fa') || getConfigApp('name') || 'تابلو قیمت'),
|
||||||
@@ -130,6 +135,23 @@ export function useTvMonitorPage() {
|
|||||||
return marked.length ? marked : list
|
return marked.length ? marked : list
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function normalizeTvProduct(raw: Product): Product {
|
||||||
|
const image = (raw as Product & { image?: string | null }).image
|
||||||
|
if (image && !raw.img) {
|
||||||
|
return { ...raw, img: image }
|
||||||
|
}
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
|
||||||
|
async function syncServerClock() {
|
||||||
|
const t0 = Date.now()
|
||||||
|
const payload = await getTvServerTime()
|
||||||
|
const t1 = Date.now()
|
||||||
|
if (!payload?.unix) return
|
||||||
|
const rttHalf = (t1 - t0) / 2
|
||||||
|
serverOffsetMs = payload.unix * 1000 + rttHalf - t1
|
||||||
|
}
|
||||||
|
|
||||||
const parsianCoins = computed(() =>
|
const parsianCoins = computed(() =>
|
||||||
catalogProducts.value
|
catalogProducts.value
|
||||||
.filter(isParsianCoin)
|
.filter(isParsianCoin)
|
||||||
@@ -207,7 +229,7 @@ export function useTvMonitorPage() {
|
|||||||
async function loadProducts() {
|
async function loadProducts() {
|
||||||
try {
|
try {
|
||||||
const list = await getTvProducts()
|
const list = await getTvProducts()
|
||||||
products.value = Array.isArray(list) ? list : []
|
products.value = (Array.isArray(list) ? list : []).map(normalizeTvProduct)
|
||||||
} catch {
|
} catch {
|
||||||
if (!products.value.length) {
|
if (!products.value.length) {
|
||||||
products.value = []
|
products.value = []
|
||||||
@@ -218,7 +240,7 @@ export function useTvMonitorPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateClock() {
|
function updateClock() {
|
||||||
const now = moment()
|
const now = moment(Date.now() + serverOffsetMs).utcOffset(210)
|
||||||
nowLabel.value = now.format('HH:mm:ss')
|
nowLabel.value = now.format('HH:mm:ss')
|
||||||
dateLabel.value = now.format('dddd jD jMMMM jYYYY')
|
dateLabel.value = now.format('dddd jD jMMMM jYYYY')
|
||||||
}
|
}
|
||||||
@@ -313,6 +335,7 @@ export function useTvMonitorPage() {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
document.documentElement.classList.add(TV_HTML_CLASS)
|
document.documentElement.classList.add(TV_HTML_CLASS)
|
||||||
|
await syncServerClock()
|
||||||
updateClock()
|
updateClock()
|
||||||
hideCursorSoon()
|
hideCursorSoon()
|
||||||
clockTimer = setInterval(updateClock, 1000)
|
clockTimer = setInterval(updateClock, 1000)
|
||||||
@@ -325,6 +348,7 @@ export function useTvMonitorPage() {
|
|||||||
void loadProducts()
|
void loadProducts()
|
||||||
listTimer = setInterval(() => {
|
listTimer = setInterval(() => {
|
||||||
void loadProducts()
|
void loadProducts()
|
||||||
|
void syncServerClock()
|
||||||
}, REFRESH_MS)
|
}, REFRESH_MS)
|
||||||
|
|
||||||
document.title = `${brandName.value} | تابلو قیمت`
|
document.title = `${brandName.value} | تابلو قیمت`
|
||||||
|
|||||||
Reference in New Issue
Block a user