add banners
This commit is contained in:
Vendored
+1
@@ -73,5 +73,6 @@ declare module '@vue/runtime-core' {
|
|||||||
CustomTag: typeof import('./src/components/CustomTag.vue')['default']
|
CustomTag: typeof import('./src/components/CustomTag.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
UploadPic: typeof import('./src/components/UploadPic.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,3 +19,9 @@ export const updateEditConst = async (obj: Const) =>
|
|||||||
export const createAddConst = async (obj: Const) => await useAxios.post<Const>(`/admin/const`, obj)
|
export const createAddConst = async (obj: Const) => await useAxios.post<Const>(`/admin/const`, obj)
|
||||||
|
|
||||||
export const deleteConst = async (id: number) => await useAxios.delete<boolean>(`/api/const/${id}`)
|
export const deleteConst = async (id: number) => await useAxios.delete<boolean>(`/api/const/${id}`)
|
||||||
|
|
||||||
|
export const updateBannerConst = async (obj: Const) =>
|
||||||
|
await useAxios.put<Const>(`/admin/const/2`, obj)
|
||||||
|
|
||||||
|
export const getHeaderBannerConst = async () =>
|
||||||
|
await useAxios.get<Array<Const>>('/api/const/key/header_banner')
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<template>
|
||||||
|
<div style="overflow: hidden">
|
||||||
|
<a-upload
|
||||||
|
class="relative fix-capture"
|
||||||
|
name="file"
|
||||||
|
:show-upload-list="false"
|
||||||
|
list-type="picture-card"
|
||||||
|
:action="url"
|
||||||
|
:headers="fileHeader"
|
||||||
|
accept="image/*"
|
||||||
|
@change="handelChange"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
@click="deleteAvatar"
|
||||||
|
v-if="imageUrl"
|
||||||
|
style="position: absolute; bottom: 10px; left: 0; z-index: 10"
|
||||||
|
>
|
||||||
|
<BaseIcon icon="vuesax-linear:trash" class="text-red-600 cursor-pointer" />
|
||||||
|
</div>
|
||||||
|
<div class="relative" v-if="imageUrl">
|
||||||
|
<img
|
||||||
|
:src="imageUrl"
|
||||||
|
alt="avatar"
|
||||||
|
style="min-width: 104px; max-height: 104px; object-fit: cover; display: inline"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="!imageUrl" class="flex flex-col items-center">
|
||||||
|
<span>
|
||||||
|
<slot name="TitlePic">افزودن عکس</slot>
|
||||||
|
</span>
|
||||||
|
<div v-if="loading" class="flex gap-4 items-center mt-2">
|
||||||
|
<loading-outlined></loading-outlined>
|
||||||
|
<span>{{ percent }} %</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { uploadURL } from '@root/config'
|
||||||
|
import { onMounted, ref, watch, computed } from 'vue'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons-vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import { getToken } from '@/utils'
|
||||||
|
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
|
const percent = ref<number>(0)
|
||||||
|
const url = ref<string | undefined>(`${uploadURL}`)
|
||||||
|
const fileHeader = computed(() => ({
|
||||||
|
Authorization: `Bearer ${getToken()}`
|
||||||
|
}))
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
imageUrl?: string
|
||||||
|
compressed?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
compressed: false
|
||||||
|
})
|
||||||
|
const imageUrl = ref('')
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'remove'): void
|
||||||
|
(e: 'upload', file: any): void
|
||||||
|
(e: 'startUpload'): void
|
||||||
|
(e: 'errorUpload'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handelChange({ file, fileList }) {
|
||||||
|
if (Object.hasOwn(file, 'percent')) {
|
||||||
|
percent.value = Math.floor(file.percent)
|
||||||
|
}
|
||||||
|
fixUpload()
|
||||||
|
|
||||||
|
if (file.status === 'uploading') {
|
||||||
|
loading.value = true
|
||||||
|
emit('startUpload')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.status === 'error') {
|
||||||
|
loading.value = false
|
||||||
|
emit('errorUpload')
|
||||||
|
message.error('خطا در آپلود فایل')
|
||||||
|
}
|
||||||
|
if (file.status == 'done') {
|
||||||
|
imageUrl.value = file.response.full_src
|
||||||
|
loading.value = false
|
||||||
|
emit('upload', file.response)
|
||||||
|
message.success('فایل با موفقیت آپلود شد')
|
||||||
|
} else if (fileList.status == 'removed') {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixUpload() {
|
||||||
|
document.querySelectorAll('.fix-capture input').forEach((item) => {
|
||||||
|
item.removeAttribute('capture')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fixUpload()
|
||||||
|
})
|
||||||
|
|
||||||
|
function deleteAvatar(e) {
|
||||||
|
imageUrl.value = null
|
||||||
|
emit('remove')
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.imageUrl,
|
||||||
|
() => {
|
||||||
|
imageUrl.value = props.imageUrl
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
export interface IBanner {
|
||||||
|
show: boolean
|
||||||
|
large: string | null
|
||||||
|
medium: string | null
|
||||||
|
small: string | null
|
||||||
|
banner1: string | null
|
||||||
|
banner2: string | null
|
||||||
|
banner3: string | null
|
||||||
|
banner4: string | null
|
||||||
|
banner5: string | null
|
||||||
|
banner6: string | null
|
||||||
|
banner_1_link: string | null
|
||||||
|
banner_2_link: string | null
|
||||||
|
banner_3_link: string | null
|
||||||
|
banner_4_link: string | null
|
||||||
|
banner_5_link: string | null
|
||||||
|
banner_6_link: string | null
|
||||||
|
}
|
||||||
|
export interface IBenefits {
|
||||||
|
show: boolean
|
||||||
|
icon: string | null
|
||||||
|
caption: string | null
|
||||||
|
link: string | null
|
||||||
|
}
|
||||||
|
export interface countryCreator {
|
||||||
|
caption: string | null
|
||||||
|
key: string | null
|
||||||
|
value: string | null
|
||||||
|
}
|
||||||
|
export interface priceRange {
|
||||||
|
min: number | null
|
||||||
|
max: number | null
|
||||||
|
key: string | null
|
||||||
|
value: string | null
|
||||||
|
}
|
||||||
|
export interface Categories {
|
||||||
|
caption: number | null
|
||||||
|
key: string | null
|
||||||
|
value: string | null
|
||||||
|
link: string | null
|
||||||
|
}
|
||||||
|
export interface notificationBar {
|
||||||
|
notifBar_text: string
|
||||||
|
notifBar_show: boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">
|
||||||
|
<BaseButton size="small" type="primary" @click="addRow">
|
||||||
|
<span> افزودن بنر</span>
|
||||||
|
<BaseIcon icon="vuesax-linear:add" size="17" />
|
||||||
|
</BaseButton>
|
||||||
|
<div class="flex mt-4 flex-col gap-2">
|
||||||
|
<div class="grid grid-cols-3 gap-4">
|
||||||
|
<Card v-for="(item, index) in benefitsList">
|
||||||
|
<div class="grid grid-cols-1 gap-4 mt-3">
|
||||||
|
<div class="text">(16 در 9)</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<UploadPic
|
||||||
|
:compressed="true"
|
||||||
|
@upload="handleUpload($event, index, 'icon')"
|
||||||
|
@remove="handelRemoveFile(index, 'icon')"
|
||||||
|
:imageUrl="item.icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-2 my-2">
|
||||||
|
<span>نمایش:</span>
|
||||||
|
<a-switch v-model:checked="item.show" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-left mt-5 md:mt-0">
|
||||||
|
<BaseButton type="primary" @click="handleSubmit">
|
||||||
|
<span>ذخیره تغییرات</span>
|
||||||
|
</BaseButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Card, message } from "ant-design-vue"
|
||||||
|
import type { IBenefits } from "@/model/banner"
|
||||||
|
import UploadPic from "@/components/UploadPic.vue"
|
||||||
|
import { updateBannerConst, getHeaderBannerConst } from "@/api/consts"
|
||||||
|
|
||||||
|
const benefitsList = ref<IBenefits[]>([])
|
||||||
|
let id: number | null = null
|
||||||
|
|
||||||
|
function addRow() {
|
||||||
|
benefitsList.value.push({ icon: null, show: true, caption: null, link: null })
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRow(index: number) {
|
||||||
|
benefitsList.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleUpload(file: any, index: number, size: string) {
|
||||||
|
benefitsList.value[index][size] = file.src
|
||||||
|
}
|
||||||
|
|
||||||
|
function handelRemoveFile(index: number, size: string) {
|
||||||
|
benefitsList.value.splice(benefitsList?.value[index], 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
try {
|
||||||
|
await updateBannerConst({
|
||||||
|
id: 14,
|
||||||
|
value: JSON.stringify(benefitsList.value),
|
||||||
|
key: "header_banner"
|
||||||
|
})
|
||||||
|
message.success("اطلاعات با موفقیت ذخیره شد")
|
||||||
|
} catch (error) {
|
||||||
|
message.error("خطا در ذخیره اطلاعات")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const res = await getHeaderBannerConst()
|
||||||
|
if (res && res.length > 0) {
|
||||||
|
id = res[0].id || res[0].const_id || null
|
||||||
|
if (res[0].value) {
|
||||||
|
benefitsList.value = JSON.parse(res[0].value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('خطا در دریافت بنرها:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less"></style>
|
||||||
@@ -108,7 +108,16 @@
|
|||||||
<a-form-item label="عنوان" name="title" :rules="inputRules()">
|
<a-form-item label="عنوان" name="title" :rules="inputRules()">
|
||||||
<BasicInput v-model:value="form.title" />
|
<BasicInput v-model:value="form.title" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="دسته بندی" name="group_type" :rules="inputRules()">
|
|
||||||
|
<a-form-item v-if="getConfigApp('name') === 'garoosi'" label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||||
|
<BasicSelect
|
||||||
|
v-model:value="form.group_type"
|
||||||
|
:items="GaroosiCategoryItems"
|
||||||
|
valueName="value"
|
||||||
|
keyName="value"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-else label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||||
<BasicSelect
|
<BasicSelect
|
||||||
v-model:value="form.group_type"
|
v-model:value="form.group_type"
|
||||||
:items="categoryItems"
|
:items="categoryItems"
|
||||||
@@ -116,6 +125,7 @@
|
|||||||
keyName="value"
|
keyName="value"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="جنس فلز" name="metal_id" :rules="inputRules()">
|
<a-form-item label="جنس فلز" name="metal_id" :rules="inputRules()">
|
||||||
<BasicSelect
|
<BasicSelect
|
||||||
v-model:value="form.metal_id"
|
v-model:value="form.metal_id"
|
||||||
@@ -753,6 +763,29 @@ const unitItems: Array<Item> = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const GaroosiCategoryItems: Array<Item> = [
|
||||||
|
{
|
||||||
|
value: "طلا"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "مکعب"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "گوی"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "سکه"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "سکه پارسیان"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "مس"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "نقره"
|
||||||
|
}
|
||||||
|
];
|
||||||
const categoryItems: Array<Item> = [
|
const categoryItems: Array<Item> = [
|
||||||
{
|
{
|
||||||
value: "آبشده"
|
value: "آبشده"
|
||||||
@@ -829,6 +862,7 @@ const minusPrice = () => {
|
|||||||
|
|
||||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
import locale from "ant-design-vue/es/date-picker/locale/fa_IR";
|
import locale from "ant-design-vue/es/date-picker/locale/fa_IR";
|
||||||
|
import { getConfigApp } from '@/utils/config'
|
||||||
|
|
||||||
dayjs.extend(customParseFormat);
|
dayjs.extend(customParseFormat);
|
||||||
const customLocale = ref({
|
const customLocale = ref({
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
//import router from '@/core/router/index'
|
//import router from '@/core/router/index'
|
||||||
import { useRouterStore } from '@core/stores/router'
|
import { useRouterStore } from '@core/stores/router'
|
||||||
import type { Router, RouteRecordRaw } from 'vue-router'
|
import type { Router, RouteRecordRaw } from 'vue-router'
|
||||||
|
import { getConfigApp } from '@/utils/config'
|
||||||
//const _router = useRouter()
|
//const _router = useRouter()
|
||||||
//import { cloneDeep } from 'lodash'
|
//import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
@@ -12,6 +13,8 @@ export function setupRoutes(permissionsKeys: string[], router?: Router) {
|
|||||||
const routerStore = useRouterStore()
|
const routerStore = useRouterStore()
|
||||||
const modules = import.meta.glob<any>('./routes/*.ts', { eager: true })
|
const modules = import.meta.glob<any>('./routes/*.ts', { eager: true })
|
||||||
console.log(modules)
|
console.log(modules)
|
||||||
|
const appName = getConfigApp('name')
|
||||||
|
|
||||||
function walkInRoutes(routes: RouteRecordRaw) {
|
function walkInRoutes(routes: RouteRecordRaw) {
|
||||||
if (!routes.children) return
|
if (!routes.children) return
|
||||||
routes.children = routes?.children?.filter(
|
routes.children = routes?.children?.filter(
|
||||||
@@ -40,6 +43,17 @@ export function setupRoutes(permissionsKeys: string[], router?: Router) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterRoutesByApp(routes: RouteRecordRaw) {
|
||||||
|
if (!routes.children) return
|
||||||
|
routes.children = routes.children.filter((item) => {
|
||||||
|
if (item.name === 'discounts' && appName !== 'goldsun') return false
|
||||||
|
if (item.name === 'banners' && appName !== 'garoosi') return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
routes.children.forEach((child) => filterRoutesByApp(child))
|
||||||
|
}
|
||||||
|
|
||||||
routerStore.routes = [
|
routerStore.routes = [
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
@@ -64,6 +78,7 @@ export function setupRoutes(permissionsKeys: string[], router?: Router) {
|
|||||||
const route: RouteRecordRaw | undefined = modules[modulesKey].default ?? undefined
|
const route: RouteRecordRaw | undefined = modules[modulesKey].default ?? undefined
|
||||||
if (route) {
|
if (route) {
|
||||||
if (permissionsKeys.length && permissionsKeys[0] == '*') {
|
if (permissionsKeys.length && permissionsKeys[0] == '*') {
|
||||||
|
filterRoutesByApp(route)
|
||||||
routerStore.routes.push(route)
|
routerStore.routes.push(route)
|
||||||
if (router) {
|
if (router) {
|
||||||
router.addRoute(route)
|
router.addRoute(route)
|
||||||
@@ -74,6 +89,7 @@ export function setupRoutes(permissionsKeys: string[], router?: Router) {
|
|||||||
(route.meta.permissionsKey && permissionsKeys.includes(route.meta.permissionsKey))
|
(route.meta.permissionsKey && permissionsKeys.includes(route.meta.permissionsKey))
|
||||||
) {
|
) {
|
||||||
walkInRoutes(route)
|
walkInRoutes(route)
|
||||||
|
filterRoutesByApp(route)
|
||||||
routerStore.routes.push(route)
|
routerStore.routes.push(route)
|
||||||
if (router) {
|
if (router) {
|
||||||
router.addRoute(route)
|
router.addRoute(route)
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ const Setting: RouteRecordRaw = {
|
|||||||
component: () => import('@/pages/menuPages/discount/index.vue'),
|
component: () => import('@/pages/menuPages/discount/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
permissionsKey: 'discounts',
|
permissionsKey: 'discounts',
|
||||||
icon: 'vuesax-linear:notification-bing',
|
icon: 'vuesax-linear:discount-shape',
|
||||||
title: 'تخفیفات',
|
title: 'تخفیفات',
|
||||||
hideMenu: getConfigApp.name !== "goldsun"
|
hideMenu: getConfigApp('name') !== "goldsun"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -82,6 +82,16 @@ const Setting: RouteRecordRaw = {
|
|||||||
//hideChildren: false
|
//hideChildren: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'banners',
|
||||||
|
path: 'banners',
|
||||||
|
component: () => import('@/pages/menuPages/banners/index.vue'),
|
||||||
|
meta: {
|
||||||
|
icon: 'vuesax-linear:image',
|
||||||
|
title: 'بنرها',
|
||||||
|
hideMenu: getConfigApp('name') !== "garoosi"
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user