Skip to content

Commit db2b2e6

Browse files
authored
Merge pull request #874 from ShevArtV/mscdek
docs(mscdek): add msCDEK documentation
2 parents 7c80595 + 9477f6a commit db2b2e6

File tree

4 files changed

+946
-0
lines changed

4 files changed

+946
-0
lines changed

docs/components/mscdek/api.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# API
2+
3+
## REST API
4+
5+
Компонент предоставляет REST API через роутер MiniShop3, доступный по базовому маршруту `/api/v1/cdek`.
6+
7+
### Эндпоинты фронтенда
8+
9+
Все эндпоинты защищены `TokenMiddleware` (требуется токен ms3).
10+
11+
#### GET `/suggestions`
12+
13+
Получение подсказок адреса от сервиса DaData.
14+
15+
| Параметр | Тип | Описание |
16+
|----------|-----|----------|
17+
| `query` | string | Строка поиска (обязательно) |
18+
| `country` | string | Код или название страны |
19+
20+
#### GET `/postal-code`
21+
22+
Получение почтового индекса по названию города.
23+
24+
| Параметр | Тип | Описание |
25+
|----------|-----|----------|
26+
| `city` | string | Название города (обязательно) |
27+
| `country` | string | Код или название страны |
28+
29+
#### GET `/status`
30+
31+
Получение статуса доставки (стоимость, сроки).
32+
33+
| Параметр | Тип | Описание |
34+
|----------|-----|----------|
35+
| `postal_code` | string | Почтовый индекс |
36+
37+
#### POST `/status`
38+
39+
Сохранение данных выбранного ПВЗ в сессии. Принимает JSON-тело запроса с произвольными полями.
40+
41+
#### GET `/points`
42+
43+
Получение списка ПВЗ.
44+
45+
| Параметр | Тип | Описание |
46+
|----------|-----|----------|
47+
| `postal_code` | string | Почтовый индекс (обязательно) |
48+
| `city` | string | Название города |
49+
| `country` | string | Название страны |
50+
51+
**Ответ:**
52+
```json
53+
{
54+
"success": true,
55+
"data": {
56+
"html": "<select>...</select>",
57+
"points": [
58+
{
59+
"code": "KSD20",
60+
"name": "Краснодар, ПВЗ",
61+
"location": {
62+
"address": "ул. Уральская, 124",
63+
"latitude": 45.03,
64+
"longitude": 38.97
65+
},
66+
"work_time": "Пн-Пт 09:00-18:00"
67+
}
68+
],
69+
"postal_code": "350059",
70+
"coordinates": {
71+
"latitude": 45.03,
72+
"longitude": 38.97
73+
}
74+
}
75+
}
76+
```
77+
78+
### Эндпоинт менеджера
79+
80+
#### POST `/order/update-point`
81+
82+
Обновление выбранного ПВЗ и пересчёт стоимости доставки для существующего заказа. Доступен только для администраторов (проверка `isMember('Administrator')`).
83+
84+
| Параметр | Тип | Описание |
85+
|----------|-----|----------|
86+
| `order_id` | int | ID заказа (обязательно) |
87+
| `point` | string | Код ПВЗ |
88+
| `pointData` | object | Полный объект данных ПВЗ |
89+
90+
**Ответ:**
91+
```json
92+
{
93+
"success": true,
94+
"data": {
95+
"point": "KSD20",
96+
"delivery_cost": 346.5,
97+
"cost": 12622.5
98+
}
99+
}
100+
```
101+
102+
## Использование
103+
104+
API используется внутренними модулями компонента. Для кастомных интеграций можно обращаться к эндпоинтам напрямую через `fetch`:
105+
106+
```javascript
107+
const assetsUrl = '/assets/components/minishop3/';
108+
const response = await fetch(`${assetsUrl}api.php?route=/api/v1/cdek/points&postal_code=350059`);
109+
const data = await response.json();
110+
```
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
# Разработка
2+
3+
## Изменение стилей
4+
5+
### Неправильный вариант
6+
7+
1. Подключить свои стили ниже;
8+
2. Переопределить значения в них.
9+
10+
### Правильный вариант
11+
12+
1. Скопировать стандартный файл;
13+
2. Переопределить нужные значения;
14+
3. Указать новый путь в системной настройке `mscdek_main_frontend_css`
15+
16+
## Изменение чанков
17+
18+
Компонент использует следующие чанки:
19+
20+
1. **mscdekSuggestItem** - элемент списка подсказок адреса.
21+
Переменные: `{$value}` — текст подсказки.
22+
23+
2. **mscdekListWrap** - обёртка списка ПВЗ (обязательно должен содержать `select[name="point"]`).
24+
Переменные: `{$items}` — HTML-строка из элементов `mscdekListItem`.
25+
26+
3. **mscdekListItem** - элемент списка ПВЗ (обязательно `<option>`).
27+
Переменные: `{$code}` — код ПВЗ, `{$name}` — название, `{$location.address}` — адрес, а также все поля объекта ПВЗ из API СДЭК.
28+
29+
4. **mscdekEmpty** - отображается если ПВЗ не найдены по почтовому индексу.
30+
Переменные: нет.
31+
32+
5. **mscdekStatus** - статус доставки (сроки, стоимость).
33+
Переменные: `{$period_min}`, `{$period_max}` — сроки в днях, `{$delivery_cost}` — стоимость, `{$total_sum}`, `{$currency}` и другие поля из ответа API расчёта тарифа СДЭК.
34+
35+
## Скрыть или изменить всплывающее сообщение
36+
37+
Тексты всплывающих сообщений хранятся в файлах словарей и имеют следующие ключи:
38+
39+
* `mscdek_error`
40+
* `mscdek_choose_pvz_error_message`
41+
42+
## Изменить масштаб карты при инициализации
43+
44+
```js:line-numbers
45+
document.addEventListener('mscdek:map:show', e => {
46+
const {object} = e.detail;
47+
object.mapUpdParams['zoom'] = 10;
48+
})
49+
```
50+
51+
## Запретить выбор подсказки, если не введена улица и выбрана доставка до двери
52+
53+
```js:line-numbers
54+
document.addEventListener('mscdek:address:select:suggestion', e => {
55+
const {location, object} = e.detail;
56+
const selectedDelivery = document.querySelector('[name="delivery_id"]:checked');
57+
if (!location.data.street && selectedDelivery.value === object.config.deliveriesByType.door) {
58+
e.preventDefault();
59+
alert('Введите улицу');
60+
}
61+
})
62+
```
63+
64+
## Изменить стандартный вывод статуса на всплывающее уведомление
65+
66+
:::warning
67+
Удалите со страницы блок вывода статуса.
68+
:::
69+
70+
```js:line-numbers
71+
document.addEventListener('mscdek:status:get', e => {
72+
const {status, object} = e.detail;
73+
if (!object.statusBlock) {
74+
const message = `Доставим до ${status.calc_result.delivery_date_range.max} за ${status.delivery_cost} руб.`
75+
alert(message);
76+
}
77+
})
78+
```
79+
80+
## Показывать карту в модальном окне
81+
82+
:::warning
83+
В примере используется библиотека [Fancybox](https://fancyapps.com/fancybox/getting-started/)
84+
:::
85+
86+
#### Добавим кнопку открытия модального окна, которая изначально скрыта с помощью класса `.hide`
87+
88+
```html
89+
<button type="button" class="hide" data-fancybox data-src="#map-modal">Выбрать ПВЗ</button>
90+
```
91+
92+
#### Добавим JS, который будет менять видимость кнопки при загрузке страницы и смене способа доставки
93+
94+
:::info
95+
`3` в функции `toggleMapButton` это ID способа доставки до ПВЗ
96+
:::
97+
98+
```js:line-numbers
99+
const toggleMapButton = () => {
100+
const btn = document.querySelector('[data-src="#map-modal"]');
101+
const delivery = document.querySelector('[name="delivery_id"]:checked');
102+
if (Number(delivery.value) === 3) {
103+
btn.classList.remove('hide');
104+
} else {
105+
btn.classList.add('hide');
106+
}
107+
}
108+
109+
document.addEventListener('DOMContentLoaded', e => {
110+
toggleMapButton();
111+
})
112+
113+
document.addEventListener('change', e => {
114+
if (e.target.name === 'delivery_id') {
115+
toggleMapButton();
116+
}
117+
})
118+
```
119+
120+
#### Поместим блок вывода карты в модальное окно
121+
122+
```html:line-numbers
123+
<div id="map-modal" style="display:none;width:800px;max-width: 100%">
124+
<div data-mscdek-map class="hide"></div>
125+
</div>
126+
```
127+
128+
#### Добавим код для открытия модального окна при клике на нашу кнопку
129+
130+
```js:line-numbers
131+
document.addEventListener('click', e => {
132+
if (e.target.closest('[data-src="#map-modal"]')) {
133+
Fancybox.defaults.dragToClose = false; // чтобы можно было двигать карту мышкой или пальцем
134+
Fancybox.show([{src: "#map-modal", type: "inline"}]);
135+
}
136+
})
137+
```
138+
139+
## Вывод дополнительной информации о ПВЗ и подтверждение выбора кнопкой
140+
141+
:::info
142+
Предполагается что на странице отсутствует блок
143+
144+
```html:line-numbers
145+
<div class="hide" data-mscdek-list></div>
146+
```
147+
148+
а вместо него выведено скрытое поле
149+
150+
```html:line-numbers
151+
<input type="hidden" name="point">
152+
```
153+
154+
:::
155+
156+
#### Добавим шаблон блока дополнительной информации о ПВЗ
157+
158+
```html:line-numbers
159+
<template data-mscdek-baloon>
160+
<div data-pvz-details class="hide">
161+
<h5>Адрес:<br>
162+
<span data-mscdek-prop="address"></span>
163+
</h5>
164+
<p>Время работы:<br>
165+
<span data-mscdek-prop="work_time"></span>
166+
</p>
167+
<button id="select-pvz-btn" type="button">Выбрать</button>
168+
</div>
169+
</template>
170+
```
171+
172+
#### Добавим стили для блока с картой и для блока дополнительной информации
173+
174+
```css:line-numbers
175+
[data-mscdek-map] {
176+
position: relative;
177+
}
178+
179+
[data-pvz-details]:not(.hide) {
180+
flex-direction: column;
181+
display: flex;
182+
align-items: start;
183+
justify-content: center;
184+
position: absolute;
185+
left: 0;
186+
top: 0;
187+
height: 100%;
188+
background: rgba(0, 0, 0, .7);
189+
width: 40%;
190+
padding: 30px;
191+
color: white;
192+
}
193+
```
194+
195+
#### Поместим блок с дополнительной информацией в блок с картой после её инициализации
196+
197+
```js:line-numbers
198+
document.addEventListener('mscdek:map:init', e => {
199+
const {mapBlock} = e.detail;
200+
201+
if (!mapBlock.querySelector('[data-pvz-details]')) {
202+
const baloonTemplate = document.querySelector('[data-mscdek-baloon]');
203+
mapBlock.insertAdjacentHTML('beforeend', baloonTemplate.innerHTML);
204+
baloonTemplate.innerHTML = '';
205+
}
206+
})
207+
```
208+
209+
#### Выводим дополнительную информацию о ПВЗ
210+
211+
```js:line-numbers
212+
const showBaloon = (markerData) => {
213+
const baloon = document.querySelector('[data-pvz-details]');
214+
if (baloon) {
215+
const addressBlock = baloon.querySelector('[data-mscdek-prop="address"]');
216+
const workTimeBlock = baloon.querySelector('[data-mscdek-prop="work_time"]');
217+
const btn = baloon.querySelector('#select-pvz-btn');
218+
btn.setAttribute('data-pvz-code', markerData.code);
219+
addressBlock.innerHTML = markerData.location.address;
220+
workTimeBlock.innerHTML = markerData.work_time;
221+
222+
baloon.classList.remove('hide');
223+
return true;
224+
}
225+
return false;
226+
}
227+
228+
document.addEventListener('mscdek:map:choose', e => {
229+
const {markerData, object} = e.detail;
230+
showBaloon(markerData.properties)
231+
})
232+
```
233+
234+
#### Добавляем обработчик подтверждения выбора ПВЗ
235+
236+
```js:line-numbers
237+
document.addEventListener('click', async e => {
238+
if (e.target.closest('#select-pvz-btn')) {
239+
const pointField = document.querySelector('[name="point"]');
240+
const pvzCode = e.target.closest('#select-pvz-btn').dataset.pvzCode;
241+
const msCdekList = window.mscdek.container.getModule('list');
242+
const result = await msCdekList.pointsStore.get(msCdekList.indexField.value);
243+
const selectedPoint = result.data.points.find(point => point.code === pvzCode);
244+
pointField && (pointField.value = selectedPoint.code);
245+
pointField && pointField.dispatchEvent(new Event('change', {bubbles: true}));
246+
247+
// раскомментируйте для показа всплывающего уведомления с адресом выбранного ПВЗ
248+
/*
249+
const message = `ПВЗ: ${selectedPoint.location.address} (${selectedPoint.distance} км.)`
250+
alert(message);
251+
*/
252+
253+
/* Fancybox.close(); */ // раскомментируйте, если хотите чтобы модальное окно закрывалось после выбора
254+
}
255+
})
256+
```
257+
258+
## Панель СДЭК в карточке заказа (админка)
259+
260+
При редактировании заказа с доставкой СДЭК в админке MiniShop3 появляется вкладка **СДЭК**. Она регистрируется через `window.MS3OrderTabsRegistry.register()` и содержит:
261+
262+
- Стоимость доставки
263+
- Код выбранного ПВЗ
264+
- Список ПВЗ с расстоянием от адреса заказа
265+
- Карту с маркерами ПВЗ (если настроен Яндекс.Карты API ключ)
266+
- Кнопку **Пересчитать доставку** — сохраняет новый ПВЗ и пересчитывает стоимость без перезагрузки страницы
267+
268+
Вкладка отображается только для заказов с доставкой класса `MsCdek\CdekDelivery`.
269+
270+
Обновление ПВЗ из админки вызывает эндпоинт `POST /api/v1/cdek/order/update-point`, который обновляет `address.properties` и пересчитывает стоимость через `MsCdek::recalculateCost()`.
271+
272+
## Изменить маркер для ПВЗ с чётным ID
273+
274+
```js:line-numbers
275+
document.addEventListener('mscdek:marker:create', e => {
276+
const {marker, markerData} = e.detail;
277+
if (markerData.id % 2 === 0) {
278+
marker.querySelector('img').src = 'assets/marker.png';
279+
}
280+
})
281+
```

0 commit comments

Comments
 (0)