fix some bugs
This commit is contained in:
+29
-5
@@ -354,7 +354,8 @@
|
||||
var CONFIG_COOKIE_NAME = 'APP_CONFIG_CACHE'
|
||||
var CONFIG_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 7
|
||||
|
||||
var TOKEN_STORAGE_KEY = 'liman_user_token'
|
||||
var TOKEN_STORAGE_KEY = 'customer_session_token'
|
||||
var LEGACY_TOKEN_SUFFIX = '_app_token'
|
||||
var VERSION_SUCCESS_URL_KEY = 'VERSION_Success_URL'
|
||||
var VERSION_STORAGE_KEY = 'version'
|
||||
var CONFIG_SUCCESS_URL_KEY = 'CONFIG_Success_URL'
|
||||
@@ -575,14 +576,37 @@
|
||||
})
|
||||
}
|
||||
|
||||
function collectAuthTokensForPreserve() {
|
||||
var preserved = {}
|
||||
|
||||
var session = localStorage.getItem(TOKEN_STORAGE_KEY)
|
||||
if (session) {
|
||||
preserved[TOKEN_STORAGE_KEY] = session
|
||||
}
|
||||
|
||||
for (var i = 0; i < localStorage.length; i++) {
|
||||
var key = localStorage.key(i)
|
||||
if (!key) continue
|
||||
if (key.length >= LEGACY_TOKEN_SUFFIX.length &&
|
||||
key.slice(-LEGACY_TOKEN_SUFFIX.length) === LEGACY_TOKEN_SUFFIX) {
|
||||
var value = localStorage.getItem(key)
|
||||
if (value) {
|
||||
preserved[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preserved
|
||||
}
|
||||
|
||||
function clearLocalStorageExceptToken() {
|
||||
var token = localStorage.getItem(TOKEN_STORAGE_KEY)
|
||||
var preserved = collectAuthTokensForPreserve()
|
||||
|
||||
localStorage.clear()
|
||||
|
||||
if (token) {
|
||||
localStorage.setItem(TOKEN_STORAGE_KEY, token)
|
||||
}
|
||||
Object.keys(preserved).forEach(function (key) {
|
||||
localStorage.setItem(key, preserved[key])
|
||||
})
|
||||
}
|
||||
|
||||
async function clearServiceWorkersAndCaches() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { applyProjectTheme } from '@/utils/config'
|
||||
import { useSettingsStore } from '@/store/settingsStore'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { getToken } from '@/utils'
|
||||
import '@/utils/useDateTime'
|
||||
import { initPriceRealtimeChannel } from '@/utils/priceSocket'
|
||||
|
||||
type BootWindow = Window & {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { AppConfig } from '@/config/types'
|
||||
import { resetVersionCheckCache } from '@/config/load-version'
|
||||
import { clearCachedPwaManifest } from '@/modules/pwa/manifest-cache'
|
||||
import { getTokenName } from '@/themeConfig'
|
||||
import { collectAuthTokensForPreserve } from '@/utils/authToken'
|
||||
|
||||
const CONFIG_COOKIE_NAME = 'APP_CONFIG_CACHE'
|
||||
const CONFIG_SUCCESS_URL_KEY = 'CONFIG_Success_URL'
|
||||
@@ -201,13 +201,12 @@ export async function clearBrowserRuntimeCaches() {
|
||||
}
|
||||
|
||||
function clearLocalStorageExceptToken() {
|
||||
const tokenKey = getTokenName()
|
||||
const token = localStorage.getItem(tokenKey)
|
||||
const preserved = collectAuthTokensForPreserve()
|
||||
|
||||
localStorage.clear()
|
||||
|
||||
if (token) {
|
||||
localStorage.setItem(tokenKey, token)
|
||||
for (const [key, value] of Object.entries(preserved)) {
|
||||
localStorage.setItem(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,13 +241,13 @@ export async function refreshConfigCacheForVersion(
|
||||
|
||||
startVersionUpdateLock(normalizedVersion)
|
||||
|
||||
const freshConfig = await fetchConfig()
|
||||
|
||||
clearCachedAppConfig()
|
||||
clearAppStorageExceptToken()
|
||||
clearAllBootstrapFetchCaches()
|
||||
await clearBrowserRuntimeCaches()
|
||||
|
||||
const freshConfig = await fetchConfig()
|
||||
|
||||
writeCachedAppConfig(freshConfig)
|
||||
finishVersionUpdateBeforeReload(normalizedVersion)
|
||||
|
||||
@@ -261,13 +260,13 @@ export async function resetAppAndRefreshConfig(
|
||||
) {
|
||||
const normalizedVersion = getNormalizedVersion(version)
|
||||
|
||||
const freshConfig = await fetchConfig()
|
||||
|
||||
clearCachedAppConfig()
|
||||
clearAppStorageExceptToken()
|
||||
clearAllBootstrapFetchCaches()
|
||||
await clearBrowserRuntimeCaches()
|
||||
|
||||
const freshConfig = await fetchConfig()
|
||||
|
||||
writeCachedAppConfig(freshConfig)
|
||||
|
||||
if (normalizedVersion) {
|
||||
|
||||
@@ -73,6 +73,8 @@ async function checkRemoteVersion() {
|
||||
}
|
||||
|
||||
async function startUpdate(version: string) {
|
||||
const configBackup = window.__APP_CONFIG__
|
||||
|
||||
try {
|
||||
isUpdating.value = true
|
||||
hasError.value = false
|
||||
@@ -95,6 +97,11 @@ async function startUpdate(version: string) {
|
||||
} catch (error) {
|
||||
console.error('Version update failed:', error)
|
||||
|
||||
if (configBackup) {
|
||||
window.__APP_CONFIG__ = configBackup
|
||||
setAppConfig(configBackup)
|
||||
}
|
||||
|
||||
cancelVersionUpdateLock()
|
||||
|
||||
isUpdating.value = false
|
||||
|
||||
+7
-2
@@ -31,9 +31,14 @@ export function getAvatarUploadURL(): string {
|
||||
return base ? `${base}/customer/avatar` : '/customer/avatar'
|
||||
}
|
||||
|
||||
/** کلید ثابت — وقتی config پاک میشود (بهروزرسانی نسخه) توکن گم نشود */
|
||||
export const SESSION_AUTH_KEY = 'customer_session_token'
|
||||
|
||||
export const LEGACY_TOKEN_SUFFIX = '_app_token'
|
||||
|
||||
export function getTokenName(): string {
|
||||
const name = getConfigApp('name') || readCachedTenantField('name') || 'app'
|
||||
return `${name}_app_token`
|
||||
const name = readCachedTenantField('name') || getConfigApp('name') || 'app'
|
||||
return `${name}${LEGACY_TOKEN_SUFFIX}`
|
||||
}
|
||||
|
||||
/** @deprecated از getTokenName() استفاده کن */
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
getTokenName,
|
||||
LEGACY_TOKEN_SUFFIX,
|
||||
SESSION_AUTH_KEY,
|
||||
} from '@/themeConfig'
|
||||
|
||||
function legacyTokenKeys(): string[] {
|
||||
const keys: string[] = []
|
||||
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i)
|
||||
if (key?.endsWith(LEGACY_TOKEN_SUFFIX)) {
|
||||
keys.push(key)
|
||||
}
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
/** خواندن توکن — اول session ثابت، بعد کلید tenant، بعد هر *_app_token */
|
||||
export function findStoredAuthToken(): string | null {
|
||||
const session = localStorage.getItem(SESSION_AUTH_KEY)
|
||||
if (session) {
|
||||
return session
|
||||
}
|
||||
|
||||
const named = localStorage.getItem(getTokenName())
|
||||
if (named) {
|
||||
localStorage.setItem(SESSION_AUTH_KEY, named)
|
||||
return named
|
||||
}
|
||||
|
||||
for (const key of legacyTokenKeys()) {
|
||||
const value = localStorage.getItem(key)
|
||||
if (value) {
|
||||
localStorage.setItem(SESSION_AUTH_KEY, value)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function persistAuthToken(token: string | undefined | null) {
|
||||
if (!token) {
|
||||
return
|
||||
}
|
||||
|
||||
localStorage.setItem(SESSION_AUTH_KEY, token)
|
||||
localStorage.setItem(getTokenName(), token)
|
||||
}
|
||||
|
||||
export function clearStoredAuthTokens() {
|
||||
localStorage.removeItem(SESSION_AUTH_KEY)
|
||||
|
||||
for (const key of legacyTokenKeys()) {
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
|
||||
export function collectAuthTokensForPreserve(): Record<string, string> {
|
||||
const preserved: Record<string, string> = {}
|
||||
|
||||
for (const key of [SESSION_AUTH_KEY, getTokenName(), ...legacyTokenKeys()]) {
|
||||
const value = localStorage.getItem(key)
|
||||
if (value) {
|
||||
preserved[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return preserved
|
||||
}
|
||||
+12
-5
@@ -1,4 +1,8 @@
|
||||
import { getTokenName } from '@/themeConfig'
|
||||
import {
|
||||
clearStoredAuthTokens,
|
||||
findStoredAuthToken,
|
||||
persistAuthToken,
|
||||
} from '@/utils/authToken'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import {useConfigStore} from "@/store/configStore";
|
||||
@@ -11,7 +15,10 @@ const nameProject= getConfigApp('name')
|
||||
|
||||
|
||||
function setToken(token: string | undefined) {
|
||||
return localStorage.setItem(getTokenName(), token)
|
||||
if (!token) {
|
||||
return
|
||||
}
|
||||
return persistAuthToken(token)
|
||||
}
|
||||
|
||||
function setFingerPrint(val: string | undefined) {
|
||||
@@ -19,7 +26,7 @@ function setFingerPrint(val: string | undefined) {
|
||||
}
|
||||
|
||||
function getToken() {
|
||||
return localStorage.getItem(getTokenName())
|
||||
return findStoredAuthToken()
|
||||
}
|
||||
|
||||
function getIsActiveFingerprint() {
|
||||
@@ -56,7 +63,7 @@ export async function authenticateWithFingerprint() {
|
||||
const credentialId = btoa(String.fromCharCode(...new Uint8Array(credential.rawId)))
|
||||
|
||||
// بازیابی توکن از localStorage
|
||||
const token = localStorage.getItem(`${nameProject}_app_token`)
|
||||
const token = findStoredAuthToken()
|
||||
|
||||
if (!token) {
|
||||
console.warn('توکن پیدا نشد، عملیات متوقف شد.')
|
||||
@@ -78,7 +85,7 @@ export async function authenticateWithFingerprint() {
|
||||
|
||||
|
||||
function removeToken() {
|
||||
return localStorage.removeItem(getTokenName())
|
||||
return clearStoredAuthTokens()
|
||||
}
|
||||
|
||||
function removeFingerPrint() {
|
||||
|
||||
+69
-16
@@ -3,6 +3,7 @@ import { baseURL as fallbackBaseURL } from '@/themeConfig'
|
||||
import type {AxiosRequestConfig, AxiosResponse} from 'axios'
|
||||
import axios from 'axios'
|
||||
import {getToken, removeToken} from "@/utils";
|
||||
import { findStoredAuthToken } from '@/utils/authToken';
|
||||
import router from "@/router";
|
||||
import {get} from 'lodash'
|
||||
import {message} from "ant-design-vue";
|
||||
@@ -25,6 +26,58 @@ const _axios = axios.create({
|
||||
headers,
|
||||
})
|
||||
|
||||
let sessionVerifyTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let sessionVerifyInFlight = false
|
||||
|
||||
function requestPath(config: { url?: string; baseURL?: string } | undefined): string {
|
||||
const raw = String(config?.url ?? '')
|
||||
if (raw.startsWith('http')) {
|
||||
try {
|
||||
return new URL(raw).pathname
|
||||
} catch {
|
||||
return raw
|
||||
}
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
async function logoutToLogin() {
|
||||
removeToken()
|
||||
if (router.currentRoute.value.name !== 'login') {
|
||||
await router.push({ name: 'login' })
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleSessionVerify() {
|
||||
if (sessionVerifyTimer) {
|
||||
return
|
||||
}
|
||||
|
||||
sessionVerifyTimer = setTimeout(async () => {
|
||||
sessionVerifyTimer = null
|
||||
if (sessionVerifyInFlight) {
|
||||
return
|
||||
}
|
||||
|
||||
const token = findStoredAuthToken()
|
||||
if (!token) {
|
||||
return
|
||||
}
|
||||
|
||||
sessionVerifyInFlight = true
|
||||
try {
|
||||
await _axios.get('/customer/me', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
meta: { errorMessage: 'none' },
|
||||
})
|
||||
} catch {
|
||||
await logoutToLogin()
|
||||
} finally {
|
||||
sessionVerifyInFlight = false
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
_axios.interceptors.request.use(
|
||||
(config) => {
|
||||
const runtimeBaseURL = getConfigApp('node_server')
|
||||
@@ -54,24 +107,24 @@ _axios.interceptors.response.use(
|
||||
return response.data
|
||||
},
|
||||
async function (error) {
|
||||
console.log('this is axios error', error)
|
||||
if (error.response.data && error.response.data.message) {
|
||||
checkStatus(error.response.data.message, error.response.status, error.config)
|
||||
const response = error.response
|
||||
const config = error.config
|
||||
|
||||
if (response?.data?.message && response.status !== 401) {
|
||||
checkStatus(response.data.message, response.status, config)
|
||||
}
|
||||
|
||||
//console.log('this is error on interceptors.response', error.config.meta)
|
||||
/* if (error.response && (error.response.status == 401 || error.response.status == 400)) {
|
||||
//debugger
|
||||
removeToken()
|
||||
router.push({ name: 'login' })
|
||||
}*/
|
||||
if (error.response && error.response.status == 401) {
|
||||
///removeToken()
|
||||
console.log('error')
|
||||
removeToken()
|
||||
// router.push({ name: 'login' })
|
||||
await router.push({name: 'login'})
|
||||
} else if (error.response && error.response.status == 403) {
|
||||
if (response?.status === 401) {
|
||||
const hadAuth = Boolean(config?.headers?.Authorization)
|
||||
if (hadAuth) {
|
||||
const path = requestPath(config)
|
||||
if (path.includes('/customer/me')) {
|
||||
await logoutToLogin()
|
||||
} else {
|
||||
scheduleSessionVerify()
|
||||
}
|
||||
}
|
||||
} else if (response?.status === 403) {
|
||||
message.error('شما دسترسی لازم برای انجام این عملیات را ندارید')
|
||||
} else if (error.response && error.response.status == 422) {
|
||||
// message.error(error.response.data?.message)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
ref="headerRef"
|
||||
class="app-header px-4 py-2 top-0 z-30 wrap-fix background-color-transition"
|
||||
:class="{ 'app-header--scrolled': scrollY > 50 }"
|
||||
:class="{ 'app-header--scrolled': isHeaderScrolled }"
|
||||
>
|
||||
<div class="flex justify-between my-2 items-center">
|
||||
<div v-if="isBoursecuProject()" class="flex items-center gap-2">
|
||||
@@ -40,47 +40,126 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import router from '@/router'
|
||||
import Icon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import { getProjectMobileNavLogo, isBoursecuProject } from '@/utils/config'
|
||||
|
||||
const headerRef = ref<HTMLElement | null>(null)
|
||||
const scrollY = ref(0)
|
||||
const isHeaderScrolled = ref(false)
|
||||
|
||||
const userStore = useUserStore()
|
||||
const projectLogo = computed(() => getProjectMobileNavLogo())
|
||||
|
||||
let intersectionObserver: IntersectionObserver | null = null
|
||||
let scrollTarget: HTMLElement | Window | null = null
|
||||
|
||||
function isScrollableOverflow(value: string) {
|
||||
return value === 'auto' || value === 'scroll' || value === 'overlay'
|
||||
}
|
||||
|
||||
function findScrollParent(el: HTMLElement | null): HTMLElement | null {
|
||||
let node = el?.parentElement ?? null
|
||||
|
||||
while (node) {
|
||||
const { overflowY } = getComputedStyle(node)
|
||||
if (overflowY === 'auto' || overflowY === 'scroll') {
|
||||
const style = getComputedStyle(node)
|
||||
const overflowY = style.overflowY
|
||||
const overflow = style.overflow
|
||||
|
||||
if (isScrollableOverflow(overflowY) || isScrollableOverflow(overflow)) {
|
||||
return node
|
||||
}
|
||||
|
||||
node = node.parentElement
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function updateScrollY() {
|
||||
if (!scrollTarget) return
|
||||
scrollY.value = scrollTarget instanceof Window ? scrollTarget.scrollY : scrollTarget.scrollTop
|
||||
function resolveScrollRoot(): HTMLElement | null {
|
||||
const header = headerRef.value
|
||||
if (!header) return null
|
||||
|
||||
const chromeInner = header.closest('.main-with-bottom-chrome-inner')
|
||||
if (chromeInner instanceof HTMLElement) {
|
||||
return chromeInner
|
||||
}
|
||||
|
||||
const fromParent = findScrollParent(header)
|
||||
if (fromParent) {
|
||||
return fromParent
|
||||
}
|
||||
|
||||
const global = document.querySelector('.main-with-bottom-chrome-inner')
|
||||
return global instanceof HTMLElement ? global : null
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
scrollTarget = findScrollParent(headerRef.value) ?? window
|
||||
scrollTarget.addEventListener('scroll', updateScrollY, { passive: true })
|
||||
updateScrollY()
|
||||
function resolveScrollSentinel(scrollRoot: HTMLElement | null): HTMLElement | null {
|
||||
if (!scrollRoot) return null
|
||||
|
||||
const local = scrollRoot.querySelector('.app-header-scroll-sentinel')
|
||||
if (local instanceof HTMLElement) {
|
||||
return local
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function updateScrollStateFromPosition() {
|
||||
if (!scrollTarget) return
|
||||
|
||||
const next =
|
||||
scrollTarget instanceof Window
|
||||
? scrollTarget.scrollY > 50
|
||||
: scrollTarget.scrollTop > 50
|
||||
|
||||
isHeaderScrolled.value = next
|
||||
}
|
||||
|
||||
function teardownScrollTracking() {
|
||||
intersectionObserver?.disconnect()
|
||||
intersectionObserver = null
|
||||
|
||||
scrollTarget?.removeEventListener('scroll', updateScrollStateFromPosition)
|
||||
scrollTarget = null
|
||||
}
|
||||
|
||||
function setupScrollTracking() {
|
||||
teardownScrollTracking()
|
||||
|
||||
const scrollRoot = resolveScrollRoot()
|
||||
const sentinel = resolveScrollSentinel(scrollRoot)
|
||||
|
||||
if (scrollRoot && sentinel && typeof IntersectionObserver !== 'undefined') {
|
||||
intersectionObserver = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
isHeaderScrolled.value = !entry?.isIntersecting
|
||||
},
|
||||
{
|
||||
root: scrollRoot,
|
||||
threshold: 0,
|
||||
rootMargin: '-1px 0px 0px 0px',
|
||||
},
|
||||
)
|
||||
intersectionObserver.observe(sentinel)
|
||||
return
|
||||
}
|
||||
|
||||
scrollTarget = scrollRoot ?? window
|
||||
scrollTarget.addEventListener('scroll', updateScrollStateFromPosition, { passive: true })
|
||||
updateScrollStateFromPosition()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
setupScrollTracking()
|
||||
await nextTick()
|
||||
setupScrollTracking()
|
||||
requestAnimationFrame(() => setupScrollTracking())
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
scrollTarget?.removeEventListener('scroll', updateScrollY)
|
||||
teardownScrollTracking()
|
||||
})
|
||||
|
||||
defineEmits<{
|
||||
@@ -93,10 +172,8 @@ defineEmits<{
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* عین bg-white shadow opacity-95 قبلی */
|
||||
.app-header--scrolled {
|
||||
background-color: #fff;
|
||||
opacity: 0.95;
|
||||
background-color: rgba(255, 255, 255, 0.92);
|
||||
box-shadow:
|
||||
0 1px 3px 0 rgb(0 0 0 / 0.1),
|
||||
0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
class="w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in"
|
||||
:class="[
|
||||
isBoursecuLayout ? 'mt-8 pt-2' : 'mt-6',
|
||||
variant === 'mobile' ? 'pb-45' : 'mt-0 pb-0',
|
||||
variant === 'mobile' ? 'pb-20' : 'mt-0 pb-0',
|
||||
]"
|
||||
>
|
||||
<div class="text-center w-full">
|
||||
|
||||
@@ -356,7 +356,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in mt-6 pb-45">
|
||||
<div class=" w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in mt-6 pb-20">
|
||||
<div class="text-center w-full">
|
||||
<div>بالاترین قیمت امروز</div>
|
||||
<div v-if="lastPrice" class="flex flex-col items-center mt-3">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="mt-10 p-5">
|
||||
<div class="mt-10 p-5 home-mobile-root">
|
||||
<div class="app-header-scroll-sentinel" aria-hidden="true" />
|
||||
<component :is="layoutComponent" :layout-config="layoutConfig" />
|
||||
<AppHeader @logout="$emit('logout')" />
|
||||
</div>
|
||||
@@ -21,3 +22,15 @@ defineEmits<{
|
||||
const layoutComponent = computed(() => resolveHomeLayoutComponent())
|
||||
const layoutConfig = computed(() => getHomeLayoutConfig(getHomeLayoutId()))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.home-mobile-root {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app-header-scroll-sentinel {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
+17
-1
@@ -23,7 +23,7 @@ export default defineConfig({
|
||||
enabled: true
|
||||
},
|
||||
workbox: {
|
||||
cleanupOutdatedCaches: false,
|
||||
cleanupOutdatedCaches: true,
|
||||
clientsClaim: true,
|
||||
skipWaiting: true
|
||||
},
|
||||
@@ -93,6 +93,22 @@ export default defineConfig({
|
||||
server: {
|
||||
port: 3100,
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (
|
||||
id.includes('node_modules/moment-jalaali')
|
||||
|| id.includes('node_modules/jalaali-js')
|
||||
|| id.includes('node_modules/moment/')
|
||||
|| id.includes('/src/utils/useDateTime')
|
||||
) {
|
||||
return 'datetime'
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// base: `/single`,
|
||||
// base: `/customer`,
|
||||
css: {
|
||||
|
||||
Reference in New Issue
Block a user