kashkool ver 1
This commit is contained in:
Vendored
+1
@@ -8,6 +8,7 @@ export {}
|
|||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AButton: typeof import('ant-design-vue/es')['Button']
|
AButton: typeof import('ant-design-vue/es')['Button']
|
||||||
|
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
||||||
ADivider: typeof import('ant-design-vue/es')['Divider']
|
ADivider: typeof import('ant-design-vue/es')['Divider']
|
||||||
AForm: typeof import('ant-design-vue/es')['Form']
|
AForm: typeof import('ant-design-vue/es')['Form']
|
||||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||||
|
|||||||
+73
-17
@@ -84,15 +84,24 @@
|
|||||||
<div class="home_appear_fade_in_up mt-2">نسخه جدید منتشر شد!</div>
|
<div class="home_appear_fade_in_up mt-2">نسخه جدید منتشر شد!</div>
|
||||||
</div>
|
</div>
|
||||||
</van-dialog>
|
</van-dialog>
|
||||||
<a v-if="config.config?.name === 'garoosi'" href="https://ble.ir/garoosigallery" target="_blank"
|
<div
|
||||||
id="support-button">
|
v-if="showSupportButton && !showSplash"
|
||||||
<div class="bg-green-100 text-green-800 flex gap-2 p-2 rounded-lg mr-2">
|
id="support-button"
|
||||||
|
class="draggable-support-button"
|
||||||
|
:class="{ 'is-dragging': supportDragging }"
|
||||||
|
:style="supportButtonStyle"
|
||||||
|
@pointerdown="onSupportPointerDown"
|
||||||
|
@pointermove="onSupportPointerMove"
|
||||||
|
@pointerup="handleSupportPointerUp"
|
||||||
|
@pointercancel="onSupportPointerCancel"
|
||||||
|
>
|
||||||
|
<div class="bg-green-100 text-green-800 flex gap-2 p-2 rounded-lg">
|
||||||
<span>پشتیبانی</span>
|
<span>پشتیبانی</span>
|
||||||
<div class="w-5">
|
<div class="w-5">
|
||||||
<img src="/src/assets/img/support.png" alt=""/>
|
<img src="/logo-montzar.png" alt="" draggable="false" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!showSplash"
|
v-if="!showSplash"
|
||||||
@@ -126,6 +135,12 @@ import {useConfigStore} from "@/store/configStore";
|
|||||||
import {getProjectFontFamily, getProjectLoginLogo, getProjectSplashBackground, getProjectBrandRed, hasCustomLoginLogoLayout, isBoursecuProject} from "@/utils/config";
|
import {getProjectFontFamily, getProjectLoginLogo, getProjectSplashBackground, getProjectBrandRed, hasCustomLoginLogoLayout, isBoursecuProject} from "@/utils/config";
|
||||||
import {ConfigProvider as AConfigProvider} from 'ant-design-vue'
|
import {ConfigProvider as AConfigProvider} from 'ant-design-vue'
|
||||||
import BasicIcon from "@/components/BasicPacks/BasicIcon.vue";
|
import BasicIcon from "@/components/BasicPacks/BasicIcon.vue";
|
||||||
|
import {
|
||||||
|
buildSupportMessengerUrl,
|
||||||
|
loadSupportMessengerFromSettings,
|
||||||
|
supportMessengerSettings,
|
||||||
|
} from '@/utils/supportMessenger'
|
||||||
|
import {useDraggableFloatingButton} from '@/utils/useDraggableFloatingButton'
|
||||||
|
|
||||||
const {offlineReady, needRefresh, updateServiceWorker} = useRegisterSW()
|
const {offlineReady, needRefresh, updateServiceWorker} = useRegisterSW()
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
@@ -139,6 +154,44 @@ const splashIndicator = computed(() => h(LoadingOutlined, {
|
|||||||
const showSplash = ref(true)
|
const showSplash = ref(true)
|
||||||
const popVisible = ref(false)
|
const popVisible = ref(false)
|
||||||
|
|
||||||
|
const GAROOSI_SUPPORT_URL = 'https://ble.ir/garoosigallery'
|
||||||
|
|
||||||
|
const supportLink = computed(() => {
|
||||||
|
if (config.config?.name === 'garoosi') {
|
||||||
|
return GAROOSI_SUPPORT_URL
|
||||||
|
}
|
||||||
|
return buildSupportMessengerUrl(
|
||||||
|
supportMessengerSettings.value.messenger,
|
||||||
|
supportMessengerSettings.value.messengerId
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const showSupportButton = computed(() => !!supportLink.value)
|
||||||
|
|
||||||
|
const {
|
||||||
|
dragging: supportDragging,
|
||||||
|
fabStyle: supportButtonStyle,
|
||||||
|
handlePointerDown: onSupportPointerDown,
|
||||||
|
handlePointerMove: onSupportPointerMove,
|
||||||
|
handlePointerUp: supportPointerUp,
|
||||||
|
handlePointerCancel: onSupportPointerCancel
|
||||||
|
} = useDraggableFloatingButton('khaneghi_support_button_position', {
|
||||||
|
width: 110,
|
||||||
|
height: 44,
|
||||||
|
defaultBottomOffset: 120
|
||||||
|
})
|
||||||
|
|
||||||
|
function openSupportLink() {
|
||||||
|
if (!supportLink.value) return
|
||||||
|
window.open(supportLink.value, '_blank', 'noopener,noreferrer')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSupportPointerUp(event: PointerEvent) {
|
||||||
|
if (supportPointerUp(event)) {
|
||||||
|
openSupportLink()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const antTheme = computed(() => {
|
const antTheme = computed(() => {
|
||||||
if (!config.config?.color) return null
|
if (!config.config?.color) return null
|
||||||
|
|
||||||
@@ -164,6 +217,7 @@ const antTheme = computed(() => {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await config.getConfigData()
|
await config.getConfigData()
|
||||||
|
await loadSupportMessengerFromSettings()
|
||||||
console.log(config?.config?.color)
|
console.log(config?.config?.color)
|
||||||
checkVersion()
|
checkVersion()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -235,18 +289,20 @@ function update() {
|
|||||||
|
|
||||||
#support-button {
|
#support-button {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: calc(80px + var(--app-credit-footer-offset, 28px) + 12px);
|
|
||||||
right: 20px;
|
|
||||||
width: 70px;
|
|
||||||
height: 70px;
|
|
||||||
//border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
//box-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
&.is-dragging {
|
||||||
|
cursor: grabbing;
|
||||||
|
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 426px) {
|
@media screen and (min-width: 426px) {
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const DRAG_THRESHOLD = 6
|
||||||
|
|
||||||
|
type FabPosition = { x: number; y: number }
|
||||||
|
|
||||||
|
type DraggableFloatingOptions = {
|
||||||
|
width?: number
|
||||||
|
height?: number
|
||||||
|
defaultBottomOffset?: number
|
||||||
|
defaultRightOffset?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDraggableFloatingButton(
|
||||||
|
storageKey: string,
|
||||||
|
options: DraggableFloatingOptions = {}
|
||||||
|
) {
|
||||||
|
const width = options.width ?? 96
|
||||||
|
const height = options.height ?? 44
|
||||||
|
const defaultBottomOffset = options.defaultBottomOffset ?? 120
|
||||||
|
const defaultRightOffset = options.defaultRightOffset ?? 20
|
||||||
|
|
||||||
|
const pos = ref<FabPosition>({ x: 0, y: 0 })
|
||||||
|
const dragging = ref(false)
|
||||||
|
|
||||||
|
let pointerId: number | null = null
|
||||||
|
let startX = 0
|
||||||
|
let startY = 0
|
||||||
|
let originX = 0
|
||||||
|
let originY = 0
|
||||||
|
let hasMoved = false
|
||||||
|
|
||||||
|
const fabStyle = computed(() => ({
|
||||||
|
left: `${pos.value.x}px`,
|
||||||
|
top: `${pos.value.y}px`,
|
||||||
|
width: `${width}px`,
|
||||||
|
touchAction: 'none'
|
||||||
|
}))
|
||||||
|
|
||||||
|
function getViewportWidth() {
|
||||||
|
return Math.min(window.innerWidth, 426)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHorizontalOffset() {
|
||||||
|
const vw = window.innerWidth
|
||||||
|
const contentWidth = getViewportWidth()
|
||||||
|
return Math.max(0, (vw - contentWidth) / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetToDefault() {
|
||||||
|
const vh = window.innerHeight
|
||||||
|
const offsetX = getHorizontalOffset()
|
||||||
|
const contentWidth = getViewportWidth()
|
||||||
|
|
||||||
|
pos.value = {
|
||||||
|
x: offsetX + contentWidth - width - defaultRightOffset,
|
||||||
|
y: vh - defaultBottomOffset - height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampToViewport() {
|
||||||
|
const vw = window.innerWidth
|
||||||
|
const vh = window.innerHeight
|
||||||
|
|
||||||
|
pos.value = {
|
||||||
|
x: Math.max(0, Math.min(vw - width, pos.value.x)),
|
||||||
|
y: Math.max(0, Math.min(vh - height, pos.value.y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPosition() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(storageKey)
|
||||||
|
if (!raw) return false
|
||||||
|
const parsed = JSON.parse(raw) as FabPosition
|
||||||
|
if (typeof parsed?.x === 'number' && typeof parsed?.y === 'number') {
|
||||||
|
pos.value = parsed
|
||||||
|
clampToViewport()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function savePosition() {
|
||||||
|
localStorage.setItem(storageKey, JSON.stringify(pos.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResize() {
|
||||||
|
clampToViewport()
|
||||||
|
savePosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePointerDown(event: PointerEvent) {
|
||||||
|
dragging.value = true
|
||||||
|
hasMoved = false
|
||||||
|
pointerId = event.pointerId
|
||||||
|
startX = event.clientX
|
||||||
|
startY = event.clientY
|
||||||
|
originX = pos.value.x
|
||||||
|
originY = pos.value.y
|
||||||
|
;(event.currentTarget as HTMLElement)?.setPointerCapture?.(event.pointerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePointerMove(event: PointerEvent) {
|
||||||
|
if (!dragging.value || pointerId !== event.pointerId) return
|
||||||
|
|
||||||
|
const dx = event.clientX - startX
|
||||||
|
const dy = event.clientY - startY
|
||||||
|
|
||||||
|
if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) {
|
||||||
|
hasMoved = true
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.value = {
|
||||||
|
x: originX + dx,
|
||||||
|
y: originY + dy
|
||||||
|
}
|
||||||
|
clampToViewport()
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishPointer(event: PointerEvent) {
|
||||||
|
if (!dragging.value || pointerId !== event.pointerId) return false
|
||||||
|
|
||||||
|
dragging.value = false
|
||||||
|
pointerId = null
|
||||||
|
savePosition()
|
||||||
|
|
||||||
|
const clicked = !hasMoved
|
||||||
|
hasMoved = false
|
||||||
|
return clicked
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePointerUp(event: PointerEvent) {
|
||||||
|
return finishPointer(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePointerCancel(event: PointerEvent) {
|
||||||
|
finishPointer(event)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!loadPosition()) {
|
||||||
|
resetToDefault()
|
||||||
|
}
|
||||||
|
window.addEventListener('resize', handleResize)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', handleResize)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
dragging,
|
||||||
|
fabStyle,
|
||||||
|
handlePointerDown,
|
||||||
|
handlePointerMove,
|
||||||
|
handlePointerUp,
|
||||||
|
handlePointerCancel,
|
||||||
|
resetToDefault
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,8 +118,6 @@
|
|||||||
:src="getProjectNavLogo(config.config?.avatar)"
|
:src="getProjectNavLogo(config.config?.avatar)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="flex justify-center">خرید </div>
|
|
||||||
<div class="flex justify-center">محصول </div>-->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div :class="setNavColor('buyProduct')" class="tab" @click="setRoute('buyProduct')">
|
<!-- <div :class="setNavColor('buyProduct')" class="tab" @click="setRoute('buyProduct')">
|
||||||
@@ -157,17 +155,6 @@ import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
|||||||
import {useConfigStore} from "@/store/configStore";
|
import {useConfigStore} from "@/store/configStore";
|
||||||
import {getConfigApp, getProjectNavLogo, isBoursecuProject} from "@/utils/config";
|
import {getConfigApp, getProjectNavLogo, isBoursecuProject} from "@/utils/config";
|
||||||
|
|
||||||
/*onMounted(async () => {
|
|
||||||
const c = await document.getElementById("canvas1");
|
|
||||||
const ctx = c.getContext("2d");
|
|
||||||
console.log(ctx)
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(0,20);
|
|
||||||
ctx.lineTo(165,20)
|
|
||||||
ctx.arc(200, 50, 45, 3.9, 5.5, false);
|
|
||||||
ctx.lineTo(400,20)
|
|
||||||
ctx.stroke();
|
|
||||||
})*/
|
|
||||||
const currentRoute = computed(() => router.currentRoute.value.name)
|
const currentRoute = computed(() => router.currentRoute.value.name)
|
||||||
const hideMenu = computed(() => !!router.currentRoute.value.meta.hideMenu)
|
const hideMenu = computed(() => !!router.currentRoute.value.meta.hideMenu)
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
|
|||||||
Reference in New Issue
Block a user