feat: complete UI/UX improvements, vuesax icons, media server, and section management

This commit is contained in:
s.esmaeili
2026-10-23 21:06:15 +03:30
parent 49f30a96b1
commit fa61a76444
64 changed files with 4527 additions and 819 deletions
+27
View File
@@ -46,4 +46,31 @@ defineProps<{ lockBody?: boolean }>();
flex-direction: column;
overflow: hidden;
}
@media (max-width: 1023px) {
.ap {
height: auto;
min-height: 100%;
overflow: visible;
}
.ap__head {
position: sticky;
top: 52px;
z-index: 20;
padding: 10px 12px;
background: #ffffff;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.03);
}
.ap__body {
padding: 12px;
overflow: visible;
}
.ap__body--locked {
overflow: visible;
height: auto;
}
}
</style>
+9 -11
View File
@@ -1,20 +1,18 @@
<script setup lang="ts">
import { ICON_NAMES, type IconName } from "@imen/shared";
import { computed } from "vue";
import { ICONS } from "./icons";
import { RECOMMENDED_ICONS } from "@imen/shared";
import VuesaxIcon from "./VuesaxIcon.vue";
/** Picks one of the Lucide icons the website knows how to render */
const value = defineModel<string>({ required: true });
defineProps<{ allowClear?: boolean }>();
const options = ICON_NAMES.map((name) => ({ value: name, label: name }));
const current = computed(() => ICONS[value.value as IconName]);
const options = RECOMMENDED_ICONS.map((name) => ({ value: name, label: name }));
</script>
<template>
<div class="flex items-center gap-2">
<span class="flex size-8 shrink-0 items-center justify-center rounded-md border border-gray-200 text-[#ff5a00]">
<component :is="current" v-if="current" class="size-4" />
<span class="flex size-9 shrink-0 items-center justify-center rounded-lg border border-gray-200 bg-gray-50 text-[#ff5a00]">
<VuesaxIcon v-if="value" :name="value" :size="20" />
<span v-else class="text-xs text-gray-400">---</span>
</span>
<a-select
v-model:value="value"
@@ -25,9 +23,9 @@ const current = computed(() => ICONS[value.value as IconName]);
placeholder="انتخاب آیکون"
>
<template #option="{ value: name }">
<span class="flex items-center gap-2" dir="ltr">
<component :is="ICONS[name as IconName]" class="size-4" />
{{ name }}
<span class="flex items-center gap-2.5 py-0.5" dir="ltr">
<VuesaxIcon :name="name" :size="18" class="text-orange-600" />
<span class="text-xs font-mono">{{ name }}</span>
</span>
</template>
</a-select>
+31 -8
View File
@@ -1,11 +1,15 @@
<script setup lang="ts">
import VuesaxIcon from "@/components/VuesaxIcon.vue";
defineProps<{ title: string; subtitle?: string; back?: string }>();
</script>
<template>
<div class="page-header">
<div class="page-header__main">
<RouterLink v-if="back" :to="back" class="page-header__back" title="بازگشت">→</RouterLink>
<RouterLink v-if="back" :to="back" class="page-header__back" title="بازگشت">
<VuesaxIcon name="arrow-right" :size="18" />
</RouterLink>
<div class="min-w-0">
<h1 class="page-header__title">{{ title }}</h1>
<p v-if="subtitle" class="page-header__subtitle">{{ subtitle }}</p>
@@ -19,42 +23,61 @@ defineProps<{ title: string; subtitle?: string; back?: string }>();
<style scoped>
.page-header {
flex: 0 0 auto !important;
flex-shrink: 0 !important;
flex-grow: 0 !important;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 12px;
gap: 10px;
}
.page-header__main {
display: flex;
align-items: center;
gap: 10px;
gap: 8px;
min-width: 0;
}
.page-header__back {
color: #94a3b8;
display: inline-flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
border-radius: 8px;
background: #f1f5f9;
color: #475569;
text-decoration: none;
transition: color 0.15s ease;
transition: all 0.15s ease;
flex-shrink: 0;
}
.page-header__back:hover {
color: #334155;
background: #e2e8f0;
color: #0f172a;
}
.page-header__title {
margin: 0;
font-size: 18px;
font-size: 16px;
font-weight: 700;
line-height: 1.3;
color: #0f172a;
}
@media (min-width: 640px) {
.page-header__title {
font-size: 18px;
}
}
.page-header__subtitle {
margin: 2px 0 0;
font-size: 13px;
font-size: 12px;
color: #94a3b8;
line-height: 1.4;
}
.page-header__actions {
+7 -6
View File
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { computed } from "vue";
import { resolveVuesaxIcon } from "@imen/shared";
import icons from "@/icons/vuesax-linear.json";
const props = withDefaults(
defineProps<{
/** Short name (`add`) or full key (`vuesax-linear:add`) */
/** Short name (`add`), alias (`ShieldCheck`, `Phone`), or full key (`vuesax-linear:add`) */
name: string;
size?: number | string;
}>(),
@@ -14,10 +15,9 @@ const props = withDefaults(
const pack = icons as Record<string, string>;
const svgHtml = computed(() => {
const key = props.name.startsWith("vuesax-linear:")
? props.name
: `vuesax-linear:${props.name}`;
const raw = pack[key];
const resolved = resolveVuesaxIcon(props.name || "");
const key = resolved.includes(":") ? resolved : `vuesax-linear:${resolved}`;
const raw = pack[key] || pack[`vuesax-linear:${resolved.toLowerCase()}`] || pack["vuesax-linear:shield-tick"];
if (!raw) return "";
return raw
.replace(/\swidth="24"/, "")
@@ -33,7 +33,7 @@ const box = computed(() =>
<template>
<span
class="vx-icon"
class="anticon vx-icon"
:style="{ width: box, height: box }"
:aria-hidden="true"
v-html="svgHtml"
@@ -48,6 +48,7 @@ const box = computed(() =>
justify-content: center;
line-height: 0;
color: inherit;
vertical-align: -0.15em;
}
.vx-icon :deep(svg) {
+2 -104
View File
@@ -1,104 +1,2 @@
import type { IconName } from "@imen/shared";
import {
Award,
BadgeCheck,
Building2,
ClipboardCheck,
Clock,
Construction,
Ear,
Factory,
FileCheck,
FileSearch,
Flame,
Footprints,
Fuel,
Gauge,
Glasses,
Globe,
GraduationCap,
Hand,
Handshake,
HardHat,
Headset,
HeartPulse,
Leaf,
Lock,
Mail,
MapPin,
Medal,
Microscope,
Package,
PackageCheck,
Phone,
Pickaxe,
ShieldAlert,
ShieldCheck,
ShieldPlus,
Shirt,
Siren,
Star,
Target,
ThumbsUp,
TrafficCone,
TriangleAlert,
Truck,
Users,
Warehouse,
Wind,
Wrench,
Zap,
} from "lucide-vue-next";
import type { Component } from "vue";
/** Same icon set as the website's DynamicIcon (ICON_NAMES in @imen/shared) */
export const ICONS: Record<IconName, Component> = {
ShieldCheck,
ShieldPlus,
ShieldAlert,
BadgeCheck,
Award,
Medal,
PackageCheck,
Package,
Truck,
Warehouse,
Globe,
Users,
Handshake,
Headset,
ClipboardCheck,
FileCheck,
FileSearch,
GraduationCap,
Gauge,
Microscope,
Wrench,
HardHat,
Shirt,
Footprints,
Hand,
Glasses,
Ear,
Wind,
TriangleAlert,
Siren,
TrafficCone,
Construction,
Factory,
Building2,
Fuel,
Pickaxe,
Flame,
Zap,
Leaf,
HeartPulse,
Clock,
Phone,
Mail,
MapPin,
Star,
ThumbsUp,
Target,
Lock,
};
export { ICON_ALIASES, ICON_NAMES, RECOMMENDED_ICONS, resolveVuesaxIcon } from "@imen/shared";
export type { IconName } from "@imen/shared";