|
|
пре 1 месец | |
|---|---|---|
| app | пре 1 месец | |
| README.md | пре 1 месец | |
| tailwind.config.js | пре 1 месец |
Название: Интернет-магазин лакокрасочной продукции "Браер-Колор" с админ панелью, мудьтидоменной и мультиязычной структурой Тип: SPA (Single Page Application) с современным минималистичным дизайном Аналоги: Функциональность m-kraski.ru с дизайном impasto-color.ru
app/
├── index.pug (основной layout)
├── index.coffee (инициализация Vue и роутера)
├── index.styl (файл хранения описания классов по системе BEM с использованием Tailwind)
├── utils/
│ └── pouch.coffee (сервис работы с PouchDB)
├── design/
│ ├── admin.coffee (design документы админки)
│ └── site.coffee (design документы сайта)
└── pages/
├── Admin/ (админ-панель)
│ ├── Layout/ (общий layout админки)
│ ├── Slider/ (управление слайдами)
│ ├── Products/ (управление товарами)
│ ├── Clients/ (управление клиентами)
│ ├── Blog/ (управление блогом с Markdown)
│ ├── Routes/ (управление маршрутами)
│ └── Settings/ (настройки системы)
└── [пользовательские страницы]
Важна должна храниться в БД привязанная к конкретному домену/доменам theme.config.coffee:
module.exports = {
colors: {
primary: {
50: '#fef2f2', 100: '#fee2e2', 200: '#fecaca', 300: '#fca5a5',
400: '#f87171', 500: '#ef4444', 600: '#dc2626', 700: '#b91c1c',
800: '#991b1b', 900: '#7f1d1d'
}
accent: {
50: '#f0f9ff', 100: '#e0f2fe', 200: '#bae6fd', 300: '#7dd3fc',
400: '#38bdf8', 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1',
800: '#075985', 900: '#0c4a6e'
}
},
typography: {
fonts: {
sans: ['Inter', 'ui-sans-serif', 'system-ui']
},
sizes: {
xs: '0.75rem', sm: '0.875rem', base: '1rem', lg: '1.125rem',
xl: '1.25rem', '2xl': '1.5rem', '3xl': '1.875rem'
}
}
}
Организация стилей (.styl файлы):
// Использование @css для Tailwind классов
@css {
.admin--sidebar {
@apply w-64 bg-white dark:bg-gray-800 shadow-md h-screen fixed;
}
.menu-item--active {
@apply bg-primary-100 text-primary-700 dark:bg-primary-900 dark:text-primary-300;
}
}
Основной layout (app/index.pug):
div(class="")
header(class="header")
nav(class="header-nav")
div(class="header-nav-blok")
div(class="header-nav--name") {{ kompname }}
div(class="header-nav--menu")
multilevelmenu
themetoggle
main
router-view(v-slot='{ Component }')
transition(name='page-slide' mode='out-in')
component(:is='Component')
Важно: не используй многострочные вычисляемые атрибуты, приводит к ошибке. Важно: мета данные добавляются через app/index.coffe базовым тегоьм для vuejs является body, app/index.pug начинается с div, теги html, head, body ЗАПРЕЩЕНО использовать. Важно: все тексты в клиенской части беруться их документа настройки домена. в которых должна быть предусмотрена мультиязычность, документы могут иметь произвольную структуру, создаваемую в админке.
Инициализация приложения (app/index.coffee):
# Подключение файлов
globalThis.renderFns = require 'pug.json'
globalThis.stylFns = require 'styl.json'
globalThis.themeConfig = require 'app/theme.config.coffee'
# Создание Vue приложения
app = Vue.createApp({
data: ->
return {
theme: 'light'
companyName: 'Браер-Колор'
loading: false
cartItems: []
user: null
}
render: (new Function '_ctx', '_cache', renderFns['app/index.pug'])()
methods:
toggleTheme: ->
@theme = if @theme == 'light' then 'dark' else 'light'
localStorage.setItem 'theme', @theme
document.documentElement.classList.toggle 'dark'
})
# Настройка маршрутизатора
router = VueRouter.createRouter({
history: VueRouter.createWebHistory()
routes: [
{ path: '/', component: require 'app/pages/Home' }
{
path: '/admin'
component: require 'app/pages/Admin'
redirect: '/admin/Settings'
children: [
{ path: 'slider', component: require 'app/pages/Admin/Slider' }
{ path: 'products', component: require 'app/pages/Admin/Products' }
{ path: 'clients', component: require 'app/pages/Admin/Clients' }
{ path: 'blog', component: require 'app/pages/Admin/Blog' }
{ path: 'routes', component: require 'app/pages/Admin/Routes' }
{ path: 'settings', component: require 'app/pages/Admin/Settings' }
]
}
# Динамические маршруты для статей блога
{ path: '/blog/:slug', component: require './pages/BlogArticle' }
# Пользовательские маршруты
{ path: '/contacts', component: require './pages/Contacts' }
{ path: '/about', component: require './pages/About' }
]
})
app.use(router)
app.mount('#app')
PouchDBService (app/utils/pouch.coffee):
class PouchDBService
constructor: (options = {}) ->
{@localDbName, @remoteDbUrl, @userFilter, @appVersion} = options
@localDb = null
@remoteDb = null
@initialized = false
init: ->
return Promise.resolve() if @initialized
try
@localDb = new PouchDB(@localDbName || 'braer_color_cache')
await @ensureRemoteDatabase()
await @loadDesignDocs()
await @ensureDesignDocs()
# Настройка селективной синхронизации
PouchDB.sync(@remoteDb, @localDb, {
live: true,
retry: true,
filter: (doc) => @shouldSyncDocument(doc)
})
@initialized = true
return Promise.resolve()
catch error
console.error('Ошибка инициализации PouchDB:', error)
return Promise.reject(error)
# Умное получение документа - сначала локально, потом удаленно
getDocument: (docId) ->
@ensureInit()
try
return await @localDb.get(docId)
catch localError
if localError.status == 404
try
doc = await @remoteDb.get(docId)
await @localDb.put(doc) # Кэшируем для будущих запросов
return doc
catch remoteError
throw remoteError
else
throw localError
# Сохранение только в удаленную БД с проверкой существования
saveToRemote: (doc) ->
@ensureInit()
try
existingDoc = await @remoteDb.get(doc._id)
doc._rev = existingDoc._rev
return await @remoteDb.put(doc)
catch error
if error.status == 404
return await @remoteDb.put(doc)
else
throw error
# Проверка необходимости синхронизации документа
shouldSyncDocument: (doc) ->
# Всегда синхронизируем общие данные
if doc.type in ['product', 'category', 'settings', 'hero_slide', 'blog_article', 'route']
return true
# Для пользовательских данных проверяем принадлежность
if doc.type in ['order', 'user_data', 'cart']
return doc.userId == @userFilter?.userId
return false
module.exports = new PouchDBService({
localDbName: 'braer_color_cache'
remoteDbUrl: 'http://localhost:5984/braer_color_shop'
userFilter: { userId: 'current_user_id' }
appVersion: '1.0.0'
})
Выжно: при реализации кода, проверяй наличие баз данных, если их нет то создай, также проверяй наличие _desing документов, если их нет создай.
Учти возможность многодоменной разработки, и ограничения доступа в зависимости от используемого домена на клиенской части, и в админке, в зависимости от прав администратора.
Layout админки (app/pages/Admin/index.pug):
div(class="mainadmin-block")
.admin-sidebar
.p-4
h1(class="mainadmin-block--h1") Панель администратора
nav(class="mainadmin-block--nav")
a(
v-for="item in menuItems"
:key="item.id"
:href="item.path"
@click.prevent="navigateTo(item.path)"
:class="getMenuItemClass(item.id)"
)
.flex.items-center.space-x-3
component(:is="item.icon" class="w-5 h-5")
span {{ item.name }}
.admin-content
router-view
Пример компонента админки (app/pages/Admin/Slider/index.coffee):
document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss">'+stylFns['app/pages/Admin/Slider/index.styl']+'</style>')
PouchDB = require 'app/utils/pouch'
module.exports =
name: 'AdminSlider'
render: (new Function '_ctx', '_cache', renderFns['app/pages/Admin/Slider/index.pug'])()
data: ->
slides: []
showSlideModal: false
currentSlide: {
title: ''
subtitle: ''
image: ''
buttonText: 'В каталог'
buttonLink: '/catalog'
active: true
order: 0
}
mounted: ->
@loadSlides()
methods:
loadSlides: ->
PouchDB.queryView('admin', 'slides', { descending: false })
.then (result) =>
@slides = result.rows.map (row) -> row.doc
.catch (error) =>
console.error 'Ошибка загрузки слайдов:', error
@showNotification 'Ошибка загрузки слайдов', 'error'
saveSlide: ->
slideData = {
type: 'hero_slide'
...@currentSlide
updatedAt: new Date().toISOString()
}
if !slideData._id
slideData._id = "hero_slide:#{Date.now()}"
slideData.createdAt = new Date().toISOString()
slideData.order = @slides.length
PouchDB.saveToRemote(slideData)
.then (result) =>
@loadSlides() # Перезагружаем через view
@showSlideModal = false
@resetCurrentSlide()
@showNotification 'Слайд успешно сохранен'
.catch (error) =>
console.error 'Ошибка сохранения слайда:', error
@showNotification 'Ошибка сохранения слайда', 'error'
admin.coffee:
module.exports = {
_id: '_design/admin',
version: '1.1.0',
appVersion: '1.0.0',
hash: 'generated_hash_here',
views: {
slides: {
map: ((doc) ->
if doc.type == 'hero_slide'
emit(doc.order, {
_id: doc._id,
title: doc.title,
active: doc.active,
order: doc.order
})
).toString()
},
products: {
map: ((doc) ->
if doc.type == 'product'
emit([doc.category, doc.createdAt], {
_id: doc._id,
name: doc.name,
price: doc.price,
active: doc.active
})
).toString()
}
}
}
Метод импорта (app/pages/Admin/Products/index.coffee):
importProducts: ->
if !@selectedFile
@showNotification 'Выберите файл для импорта', 'error'
return
@importing = true
@importResults = null
@readFile(@selectedFile)
.then (text) =>
results = Papa.parse(text, {
header: true
delimiter: ';'
skipEmptyLines: true
encoding: 'UTF-8'
})
products = results.data.filter (row) =>
row && row['Артикул*'] && row['Название товара'] && row['Цена, руб.*']
couchProducts = products.map (product, index) =>
@transformProductData(product, index)
# Пакетное сохранение в удаленную БД
return PouchDB.bulkDocs(couchProducts)
.then (result) =>
@importResults = { success: true, processed: couchProducts.length }
@showNotification "Импортировано #{couchProducts.length} товаров"
@loadProducts() # Перезагружаем список
.catch (error) =>
console.error 'Ошибка импорта:', error
@importResults = { success: false, error: error.message }
@showNotification "Ошибка импорта: #{error.message}", 'error'
.finally =>
@importing = false
Важно: при полной реализации используй пример csv файла, При этом учти что количество полей в документе может меняться. выдели из них основные, остальные загружай по факту как свойства товара. пример csv: №;Артикул;Название товара;Цена, руб.;Цена до скидки, руб.;НДС, %;Рассрочка;Баллы за отзывы;SKU;Штрихкод (Серийный номер / EAN);Вес в упаковке, г;Ширина упаковки, мм;Высота упаковки, мм;Длина упаковки, мм;Ссылка на главное фото;Ссылки на дополнительные фото;Ссылки на фото 360;Артикул фото;Бренд;Название модели (для объединения в одну карточку);Единиц в одном товаре;Цвет товара;Название цвета;Тип;Класс опасности товара;Степень блеска покрытия;Работы;Вес товара, г;Количество товара в УЕИ;#Хештеги;Аннотация;Rich-контент JSON;Название группы;Образец цвета;Партномер;Гарантия;Страна-изготовитель;Комплектация;ТН ВЭД коды ЕАЭС;Срок годности в днях;Количество заводских упаковок;Вид краски;Объем, л;Время высыхания, часов;Вес, кг;Расход, л/м2;Назначение грунтовки;Рекомендуемое количество слоев;Расход, кг/м2;Область применения состава;Количество компонентов;Особенности ЛКМ;Макс. температура эксплуатации, С°;Материал основания;Основа краски;Основа грунтовки;Способ нанесения;Форма выпуска средства;Назначение;Тип помещения;Возможность колеровки;Вид выпуска товара;Тип растворителя;Эффект краски;Марка эмали;Можно мыть;Базис;Аэрозоль;Помещение;Название модели для шаблона наименования;Ошибка;Предупреждение 1;4673764201943;Грунтовка глубокого проникновения для стен под обои и покраску ЭкоКрас 1кг;528,00;1 056,00;20;Да;Нет;;4673764201943;1000;100;55;250;https://cdn1.ozone.ru/s3/multimedia-1-o/7663357104.jpg;"https://cdn1.ozone.ru/s3/multimedia-1-8/7663357124.jpg https://cdn1.ozone.ru/s3/multimedia-1-w/7663352504.jpg https://cdn1.ozone.ru/s3/multimedia-1-r/7663357179.jpg https://cdn1.ozone.ru/s3/multimedia-1-b/7663352483.jpg https://cdn1.ozone.ru/s3/multimedia-1-c/7663352556.jpg https://cdn1.ozone.ru/s3/multimedia-1-t/7663352537.jpg https://cdn1.ozone.ru/s3/multimedia-1-o/7663352496.jpg https://cdn1.ozone.ru/s3/multimedia-1-k/7663352492.jpg https://cdn1.ozone.ru/s3/multimedia-1-y/7663365970.jpg https://cdn1.ozone.ru/s3/multimedia-1-m/7663365922.jpg https://cdn1.ozone.ru/s3/multimedia-1-1/7663365937.jpg https://cdn1.ozone.ru/s3/multimedia-1-p/7663352533.jpg";;;ЭкоКрас;4673764201943;1;прозрачный;;Грунтовка;Не опасен;;"Внутренние;Наружные";;;#Грунтовка #ГрунтДляСтен #АкриловаяГрунтовка #СтроительныеМатериалы #ГлубокогоПроникновения #УниверсальнаяГрунтовка #АдгезионнаяГрунтовка #АнтисептическаяГрунтовка #ДляВнутреннихРабот #ДляНаружныхРабот #ДляБетона #ДляГипсокартона #ДляДерева #Быстросохнущая #Водостойкая #Укрепляющая #Противогрибковая #БелаяГрунтовка #ПодОбои #ПодШтукатурку #ПодПокраску #ПодПлитку #ДляРовныхСтен #РемонтДома #ОтделкаПомещений #СтроительныеРаботы #КачественныйРемонт #СтроительныеТовары #профессиональная_грунтовка #грунтовка_концентрат_премиум;"Премиум грунтовка глубокого проникновения для подготовки поверхностей перед финишной отделкой. Грунтовка глубокого проникновения – это универсальное средство, она проникает глубоко в структуру бетона, кирпича, штукатурки, дерева и других материалов, укрепляя их и улучшая адгезию для последующего нанесения краски, обоев или плитки.
Грунтовка для стен применяется для укрепления штукатурки и шпатлевок, препятствует появлению высолов создавая щелочестойкий слой. Идеально подходит для стен, потолков и полов в любых помещениях – от ванной и кухни до жилых комнат. Благодаря жидкой консистенции и акриловой основе, грунтовка быстро впитывается, связывая пыль и предотвращая осыпание старых покрытий.
Средство подходит для наружных работ, так как устойчива к перепадам температур и влажности. Может использоваться под наливные полы, фактурную штукатурку и кафель. Белый или прозрачный состав не оставляет разводов и не влияет на цвет финишного покрытия.
Высокое содержание полимеров позволяет получить слабо впитывающие поверхности, даже в случае осыпающихся, осмеливающихся поверхностей (известковые покрытия, песчанно-цементные штукатурки слабого качества, давно окрашенные водоэмульсионными красками поверхности). Расход 0,05-0,15 кг на 1 м 2 в один раз. Наносить в один или два слоя в зависимости от состояния подложки. Второй слой наносить непосредственно за первым для более глубокого проникновения или же после полного высыхания первого слоя. Быстро высыхает, не имеет резкого запаха и безопасна для внутренних работ.
Применяется перед покраской, поклейкой обоев или укладкой плитки, обеспечивая долговечность и качество ремонта. Незаменима для обработки бетонных, известковых и гипсовых оснований, а также при работе с волма слоем.
Используйте грунтовку глубокого проникновения – и ваши отделочные работы будут выполнены на профессиональном уровне! ";"{ ""content"": [
{
""widgetName"": ""raTextBlock"",
""title"": {
""items"": [
{
""type"": ""text"",
""content"": ""Грунтовка глубокого проникновения PRIMER 1:4 концентрат ImPasto 1кг""
}
],
""size"": ""size5"",
""color"": ""color1""
},
""theme"": ""primary"",
""padding"": ""type2"",
""gapSize"": ""m"",
""text"": {
""size"": ""size2"",
""align"": ""left"",
""color"": ""color1"",
""items"": [
{
""type"": ""text"",
""content"": ""Премиум грунтовка глубокого проникновения для подготовки поверхностей перед финишной отделкой. Грунтовка глубокого проникновения – это универсальное средство, она проникает глубоко в структуру бетона, кирпича, штукатурки, дерева и других материалов, укрепляя их и улучшая адгезию для последующего нанесения краски, обоев или плитки.\r""
},
{
""type"": ""br""
},
{
""type"": ""text"",
""content"": ""\r""
},
{
""type"": ""br""
},
{
""type"": ""text"",
""content"": ""Грунтовка для стен применяется для укрепления штукатурки и шпатлевок, препятствует появлению высолов создавая щелочестойкий слой. Идеально подходит для стен, потолков и полов в любых помещениях – от ванной и кухни до жилых комнат. Благодаря жидкой консистенции и акриловой основе, грунтовка быстро впитывается, связывая пыль и предотвращая осыпание старых покрытий. \r""
},
{
""type"": ""br""
},
{
""type"": ""text"",
""content"": ""\r""
},
{
""type"": ""br""
},
{
""type"": ""text"",
""content"": ""Средство подходит для наружных работ, так как устойчива к перепадам температур и влажности. Может использоваться под наливные полы, фактурную штукатурку и кафель. Белый или прозрачный состав не оставляет разводов и не влияет на цвет финишного покрытия.\r""
},
{
""type"": ""br""
},
{
""type"": ""text"",
""content"": ""\r""
},
{
""type"": ""br""
},
{
""type"": ""text"",
""content"": ""Высокое содержание полимеров позволяет получить слабо впитывающие поверхности, даже в случае осыпающихся, осмеливающихся поверхностей (известковые покрытия, песчанно-цементные штукатурки слабого качества, давно окрашенные водоэмульсионными красками поверхности). Расход 0,05-0,15 кг на 1 м 2 в один раз. Наносить в один или два слоя в зависимости от состояния подложки. Второй слой наносить непосредственно за первым для более глубокого проникновения или же после полного высыхания первого слоя. Быстро высыхает, не имеет резкого запаха и безопасна для внутренних работ.""
}
]
}
}
], ""version"": 0.3 }";экокрас;https://cdn1.ozone.ru/s3/multimedia-1-q/7218190898.jpg;;2 года;Россия;Грунтовка глубокого проникновения ЭкоКрас 1кг- 1шт;;990;1;;1;24;1;0,05;"Глубокого проникновения;Обеспыливающая;Пропиточная;Укрепляющая;Универсальная";;;"По бетону;Для фасадов;Для хобби и творчества;Для швов;По кирпичу;По штукатурке;Универсальная";;;;"Бетон;Газобетон;Кирпич;Пенобетон;Штукатурка";;Акриловая;"Валик;Кисть;Краскопульт;Пистолет";Готовый раствор;;"С повышенной влажностью;С умеренной влажностью;Сухое";;;;;;;;;;;;
При обработке csv загружай изображения в базу данных как attachment, также сохраняй ричконтент преобразовывая в markdown, и выводи его в качестве описания товара при его наличии. формат ссылок на attachment файлы "/d/[ИМЯ БД]/[id doc]/[имя файла]" Важно: каждый товар должен быть привязан к домену/доменам (может одновременно быть доступен на разных доменах), тоже касается статей блога.
index.pug, index.coffee, index.styl(new Function '_ctx', '_cache', renderFns[...])()theme.config.coffeeapp/index.coffee app/index.pug app/index.styl app/theme.config.coffee app/utils/pouch.db app/desing/admin.coffee app/desing/site.coffee app/pages/Home/index.coffee app/pages/Home/index.pug app/pages/Home/index.styl
Анализировать реализованный код, по git репозитарию https://gogs.osvoj.ru/oleg/s5l.ru-crm.git Проверяй промт и изменения в нём по адресу https://gogs.osvoj.ru/oleg/s5l.ru-crm/raw/master/README.md Написать файлы app/pages/Admin/index.coffee app/pages/Admin/index.pug app/pages/Admin/index.styl app/pages/Admin/Settings/index.coffee app/pages/Admin/Settings/index.pug app/pages/Admin/Settings/index.styl