В этой статье будет представлена идея реализации алгоритма внешнего SKU, что может быть не лучшим способом его реализации, но может предоставить решение для небольших партнеров, у которых нет идей.
Для тех, кто еще не понимает концепции SPU и SKU, пожалуйста, переместитеЗнакомство с SKU и SPUЧтобы понять общую концепцию, в этой статье мы не будем вдаваться в подробности.
Реализовать предварительный просмотр эффекта
Реализовать идеи
Для внешнего интерфейса, если вы еще не сделали SKU, вы можете не понять его сложности.Если вы внимательно наблюдаете, небольшой выбор спецификации содержит много трудностей в деталях реализации.Вот идея реализации для всех.см.
1. Получить внутренние данные
здесь с7Данные о товарах, предоставленные учителями в июлеНапример, поскольку данные слишком длинные, они публикуются только здесь.sku_list
Этот формат данных также является наиболее важным форматом данных во всем процессе реализации.
{
...
sku_list: [
{
"id":2,
"price":77.76,
"discount_price":null,
"online":true,
"img":"",
"title":"金属灰·七龙珠",
"spu_id":2,
"category_id":17,
"root_category_id":3,
"specs":[
{
"key_id":1,
"key":"颜色",
"value_id":45,
"value":"金属灰"
},
{
"key_id":3,
"key":"图案",
"value_id":9,
"value":"七龙珠"
},
{
"key_id":4,
"key":"尺码",
"value_id":14,
"value":"小号 S"
}
],
"code":"2$1-45#3-9#4-14",
"stock":5
},
...
],
...
}
Создайте новый класс Spu для имитации получения данных из бэкенда
class Spu {
data = []
constructor() {
this.data = this.getData();
}
getData() {
const data = sku_list; //这里的sku_list就是七月老师提供的数据中对应字段的数据,我们目前只需要这些就可以了
return data
}
}
2. Извлечение данных спецификации
Данные, которые дает нам серверная часть, являются толькоОдин продукт --- комбинированные продукты с различными характеристикамиСписок, нам нужно получить все характеристики из одного списка продуктов. Здесь мы можем увидеть данные по каждому элементуspecs
Атрибуты — это различные спецификации, содержащиеся в этом элементе, и нам нужно их извлечь.
currentSkuList = []
_getCurrentSkuList() {
this.currentSkuList = this.data.map(item => item.specs);
}
Результаты экстракции следующие
3. Транспонирование матрицы
Формат данных, которые мы извлекли,
颜色 图案 尺码
金属灰 七龙珠 小号 S
青芒色 灌篮高手 中号 M
青芒色 圣斗士 大号 L
橘黄色 七龙珠 小号 S
Но согласно рендерингам нам нужны данные в следующем формате, поэтому нам нужно преобразовать вышеуказанное矩阵
Транспонируйте, это может быть не стандартная матрица, потому что номер каждой спецификации не обязательно одинаков, здесь то же самое можно лучше понять.
_transMatrix() {
this._getCurrentSkuList();
let transResult = {};
this.currentSkuList.forEach(specs => {
specs.forEach(item => {
if(!transResult[item['key_id']]) {
transResult[item['key_id']] = {
key_id: item['key_id'],
key: item['key'],
value_list: {
[item['value_id']]: {
value_id: item['value_id'],
value: item['value'],
selected: false,
disabled: false
}
}
}
} else if (!transResult[item['key_id']].value_list[item['value_id']]) {
transResult[item['key_id']].value_list[item['value_id']] = {
value_id: item['value_id'],
value: item['value'],
selected: false,
disabled: false
}
}
})
})
return transResult;
}
Результаты конвертации
Это использует гибкость объектов JavaScript и устанавливает структуру данных.
{
key_id: { //规格id
key: string, // 规格名称
key_id: number, // 规格id
[value_list: object]: { // 规格值列表 object
value_id: { // 规格值id
value_id: number, // 规格值id
value: string, // 规格值
selected: boolean, // 是否选中
disabled: boolean, // 是否不可选
}
}
},
key_id: {...},
...
}
через паруcurrentSkuList
Пройдите и отфильтруйте текущий список отдельных продуктов и извлеките его в данные в указанном выше формате данных.Метод использования объектов вместо массивов предназначен для более удобного извлечения
Преобразование результата извлечения в формат массива
getAllSpecsList() {
const transResult = this._transMatrix();
this.allSpecsList = Object.keys(transResult).map(key => {
let obj = JSON.parse(JSON.stringify(transResult[key]));
obj.value_list = Object.keys(obj.value_list).map(vk => obj.value_list[vk]);
return obj;
})
return this.allSpecsList;
}
Транспонированная матрица становится удобным массивом, который мы используем на странице.wx:for
Цикл, если используетсяreact
илиvue
Для зацикливания также очень удобно использовать массивы.Если вы используете вышеописанный формат объекта напрямую, то нужно снова преобразовать массив, поэтому для удобства непосредственного использования делаем преобразование еще раз.
использовано вышеJSON.parse(JSON.stringify(obj))
способ сделать глубокую копию транспонированного матричного объекта,Object.keys()
а такжеArray.map
API можно легко преобразовать в массив объектов перед
результат преобразования
Tips:
Обратите внимание, что это не двумерный массив, а массив объектов той структуры данных, которую мы разработали ранее, и использование этой структуры данных позволяет нам легко получать нужные данные в будущем.
4. Самый важный шаг: обход
Я действительно не знаю, как назвать этот шаг, но это самый важный шаг во всей реализации нашего алгоритма SKU.
Логика выбораДля выбора спецификации могут быть выбраны только соответствующие необязательные части других спецификаций, а остальные не являются необязательными, и прямой визуальный эффект становится серым. Но как осуществить увязку, после выбора одной спецификации средних спецификаций может быть много.В нашем случае их две после выбора одной, а по факту их может быть и больше.Средних спецификаций может быть семь или восемь Значений для спецификации может быть даже более 10, и каждый раз может быть выбрана любая из этих спецификаций.Если все возможности яростно зациклены, эффект может быть плохим.
Вот идея, то есть в соответствии со списком SKU, предоставленным серверной частью, для определения спецификации, соответствующей необязательным частям других спецификаций, описание не очень понятно, ниже приведен пример для иллюстрации:
Знайте, что спецификация цвета青芒色
, то в других спецификациях есть только два шаблона на выбор:灌篮高手
а также圣斗士
, спецификация размера только中号 M
а также大号 L
Необязательный.
Тогда мы можем определить такую таблицу:
Итак, вы можете думать об этом таким образом, каждый раз, когда мы нажимаем, чтобы выбрать спецификацию, мы будем проходить другие спецификации.При обходе других спецификаций, если значение спецификации в этой спецификации отсутствует в нашем списке, то установите его для него. необязательный (отключен), так что вещи, о которых нам нужно заботиться, очень просты, а затем мы посмотрим
Каждый раз, когда мы нажимаем на значение спецификации, нам не нужно заботиться об этой спецификации. Например, если мы выбираем голубой, нам не нужно заботиться о других цветах. Нам нужно заботиться только о других типах спецификаций, и нам не нужно заботиться обо всех других типах спецификаций, нам нужно только заботиться о том, какая из этих спецификаций может соответствовать этой в моей текущей точке.
Пример:
Как и в приведенной выше таблице, после определения того, что цвет серый, переходим к обходу других типов спецификаций (图案
а также尺码
), при обходе паттерна нужно знать только七龙珠
Совпадение со мной, а все остальные не совпадающие выделены серым цветом.При обходе размера просто знайте小号 S
Соответствуйте мне, все остальное будет выделено серым цветом.
Если вы выберете узор, вы можете выбрать только шары дракона.После выбора шаров дракона вы можете просмотреть другие типы спецификаций.В цветах есть только цвета.青芒色
а также金属灰
Вы можете сопоставить меня, а все остальные будут выделены серым цветом.
Если вы выбираете размер, вы можете выбрать только S. В соответствии с приведенными выше правилами вы можете установить серый цвет, который не соответствует двум другим спецификациям.
Так что остальное по желанию.
Преобразуйте приведенную выше идею в код:
getSelectable() {
if(this.allSpecsList.length === 0) {
this.allSpecsList()
}
if(this.currentSkuList.length === 0) {
this.currentSkuList = this.data.map(item => item.specs);
}
const rowLength = this.allSpecsList.length;
for(let row = 0; row < rowLength; row++) {
let { key_id, key } = this.allSpecsList[row];
let columnList = this.allSpecsList[row].value_list;
this.selectable[key_id] = {
key_id,
key,
selectableList: {}
}
for (let column = 0; column < columnList.length; column++) {
let { value_id, value } = columnList[column];
this.selectable[key_id].selectableList[value_id] = {
value_id,
value,
matchItems: null
}
}
this.currentSkuList.forEach(specificSpecs => {
let matchItems = {};
let currentVlaueId = '';
specificSpecs.forEach(specsItem => {
if(specsItem.key_id !== key_id) {
matchItems[specsItem.key_id] = [specsItem]
} else {
currentVlaueId = specsItem.value_id;
}
})
if (!this.selectable[key_id].selectableList[currentVlaueId].matchItems) {
this.selectable[key_id].selectableList[currentVlaueId].matchItems = matchItems;
} else {
Object.keys(this.selectable[key_id].selectableList[currentVlaueId].matchItems).forEach(k => {
this.selectable[key_id].selectableList[currentVlaueId].matchItems[k].push(...matchItems[k])
})
}
})
}
return this.selectable;
}
результат преобразования
Здесь снова структура данных предназначена для выражения наших вышеизложенных идей с помощью гибкости объектов JavaScript.
{
key_id: { // key_id 确定当前选了哪种类型的规格
key: string, // 规格名
key_id: number, // 规格id
selectableList: { // 选择列表,用来描述我们上面画的表
value_id: { // 当前选择了哪个规格,上面key_id确定了规格种类,这里value_id确定了这种规格中的具体规格,
matchItems: { // 当前规格对应其他规格的匹配项
key_id: [ // 当前规格匹配的种类规格的种类id,
{
key_id: numebr, // 种类id,这里为了方便数组中的直接取了后端返回的数据
key: string, // 规格种类名称
value_id: number, // 匹配项中的规格id
value: string, //匹配项中的规格名称
},
...
]
}
}
}
},
key_id: {...},
...
}
После разработки структуры данных найдите способ определить другие спецификации, которым может соответствовать каждый элемент спецификации, с помощью списка отдельных продуктов, заданного внутренними данными, и, наконец, преобразовать данные в структуру данных, которую мы разработали.
5. Напишите простую бизнес-логику
Предыдущий шаг можно охарактеризовать как основную и самую сложную часть всего решения.Поскольку нужная структура данных получена, окончательный эффект может быть достигнут с помощью простой бизнес-логики.
конфигурация данных страницы
data: {
sku: null,
allSpecsList: [],
selectable: null,
selected: [],
selectedItem: null,
selectTips: '请选择:',
}
Данные инициализации
initData() {
const sku = new Sku();
const allSpecsList = sku.getAllSpecsList();
const selectable = sku.getSelectable();
this.setData({
allSpecsList,
selectable,
sku
});
},
При инициализации данных мы создаем объект экземпляра Sku, сохраняем его в данных, а затем получаемallSpecsList
(转置后的矩阵数组),来循环实现页面的规格展示。
получатьselectable
Это удобно для последующей обработки при нажатии на определенную спецификацию, обработки выделения серым цветом других спецификаций.
Реализовать отображение страницы
<wxs src="../../wxs/specs.wxs" module="s"></wxs>
<view class="container">
<view class="spu-info">
<image class="selected-img" src="{{selectedItem && selectedItem.img ? selectedItem.img : 'http://i1.sleeve.7yue.pro/assets/5605cd6c-f869-46db-afe6-755b61a0122a.png'}}"></image>
<view class="spu-container">
<text class="spu-title">双色可选</text>
<view class="spu-content">
<view class="price">$1000</view>
<view class="tips">
{{selectTips}}
</view>
</view>
</view>
</view>
<view class="select-options">
<block wx:for="{{allSpecsList}}"
wx:for-item="specs"
wx:for-index="x"
wx:key="specs.key_id"
>
<view class="specs-item">
<text class="specs-title">{{specs.key}}</text>
<view class="specs-value-list">
<block wx:for="{{specs.value_list}}"
wx:for-item="value"
wx:for-index="y"
wx:key="{{value.value_id}}"
>
<view class="value-container {{s.getButtonExtraClass(value.selected, value.disabled)}}"
data-key_id="{{specs.key_id}}"
data-value_id="{{value.value_id}}"
data-select="{{s.getButtonStatus(value.selected, value.disabled)}}"
data-x="{{x}}"
data-y="{{y}}"
bindtap="handleClickSpecs"
>
<text class="value">{{value.value}}</text>
</view>
</block>
</view>
</view>
</block>
</view>
</view>
Реализация страницы — это в основном циклический обход определяемой нами структуры данных и другие операции для отображения данных. На самом деле на странице говорить не о чем, здесь я хочу рассказать о дизайне нескольких пользовательских атрибутов данных: здесь мы превращаем кнопку в ячейку
-
data-key_id
: Идентифицирует идентификатор спецификации текущей ячейки, однозначно определяет спецификацию -
data-value_id
: Идентифицирует конкретный идентификатор спецификации текущей ячейки, уникально представляющий спецификацию. -
data-select
: реализовать функцию через wxs, передать выбранные и отключенные, чтобы определить, является ли текущая ячейка необязательной, выбранной или отключенной, -
data-x
: Используйте более простой способ определить, какой тип ячейки в данный момент нажат, вы можете только определить, что один и тот же x имеет ту же спецификацию, но вы не можете знать, какой это тип. 5:data-y
: Просто определите, что выбранная в данный момент ячейка является определенной спецификацией, определите положение, а дизайн x и y может упростить поиск положения ячейки.
Еще раз поясню, что определение стиля ячейки тоже через функцию в wxs, а выделенное и отключенное передаются для определения стиля текущей ячейки.
Что делать, когда вы нажимаете на определенную спецификацию
handleClickSpecs(event) {
const { key_id, value_id, select, x, y } = event.currentTarget.dataset
if (select === 'disabled') {
return;
}
if (select === 'selectable') {
this.data.selected.forEach((item, index) => {
if (item.x === x) {
this.data.selected.splice(index, 1)
}
})
this.data.selected.push({x, y, key_id, value_id});
this.handleSelectOneOption(x, y, key_id, value_id);
}
if (select === 'active') {
this.clearAllSelectedAndDisabled();
this.data.selected.forEach((item, index) => {
if (item.x === x && item.y === y) {
this.data.selected.splice(index, 1);
}
})
this.data.selected.forEach(item => {
this.handleSelectOneOption(item.x, item.y, item.key_id, item.value_id);
})
}
this.setData({
allSpecsList: this.data.allSpecsList,
selected: this.data.selected
})
}
При нажатии на конкретную спецификацию мы столкнемся с тремя состояниями, состояние по умолчанию является необязательным.selectable
, поэтому мы используем только два дополнительных состоянияactive
выбранное состояние иdisabled
Неполноценный. Event.currentTarget, чтобы получить, щелкнув эту ячейку, затем ееdataset
Атрибуты получают некоторые вспомогательные функции, которые мы разработали ранее, чтобы упростить получение данных текущего атрибута ячейки.
- если
disabled
состояние, возвращайтесь напрямую, потому что оно не кликабельно. - если
selectable
Статус, текущую ячейку можно щелкнуть, после нажатия необходимо пройти по другим типам ячеек, а несопоставленные выделить серым цветом. и поместите текущие выбранные данные вselected
Он сохраняется в файле для последующего использования. Здесь следует подчеркнуть, что текущий тип спецификации проходит через ранее установленную переменную x при выборе. Если он был выбран ранее, удалите его, потому что может быть выбрано только одно значение за одну спецификацию. - если
active
состояние, вам нужно установить его в необязательное состояние, когда вы щелкаете по нему. В настоящее время, если вы хотите восстановить некоторые выделенные серым цветом атрибуты как необязательные, это непросто сделать. Здесь мы выбираем простой метод, то есть чтобы сделать текущий клик изselected
удалить, затем пройтиselected
, и «щелкните» его снова (конечно, здесь используется код, чтобы щелкнуть).
Этим достигаются основные функции, из которыхhandleSelectOneOption
Код выглядит следующим образом, который реализует щелчок по ячейке, чтобы сделать другие типы спецификаций серыми.
handleSelectOneOption(x, y, key_id, value_id) {
this.data.allSpecsList[x].value_list[y].selected = true;
this.data.allSpecsList[x].value_list.forEach((specs, index) => {
if (index === y) {
specs.selected = true;
} else {
specs.selected = false;
}
})
const selectableMatchItems = this.data.selectable[key_id].selectableList[value_id].matchItems;
this.data.allSpecsList.forEach((specsRow, index) => {
if (index === x) {
return;
}
specsRow.value_list.forEach(specs => {
specs.disabled = false;
if (specs.selected) {
return;
}
const result = selectableMatchItems[specsRow.key_id].find(item => item.value_id === specs.value_id);
if (!result) {
specs.disabled = true;
}
})
})
}
С помощью параметра x мы можем узнать, что нам не нужно проходить текущую строку x (то есть этот вид атрибута). Если это не текущий тип атрибута, он будет извлечен и пройден. Если это не так соответствуют текущей спецификации, она будет недоступна. Следует обратить внимание на одну деталь. Во время процесса поседения сначала восстанавливается состояние поседения этого атрибута, чтобы эффект предыдущего поседения не повлиял на результат текущего поседения.
Щелчокactive
Когда ячейка находится в состоянии, у нас также есть подробная операция, которая заключается в восстановлении всех выделенных серым цветом и выбранных до значения по умолчанию, а затем повторной операции, также чтобы предыдущее состояние не повлияло на наши текущие результаты.
Очистить все выделенные серым цветом и выбранные коды
clearAllSelectedAndDisabled() {
this.data.allSpecsList.forEach(row => {
row.value_list.forEach(specs => {
specs.selected = false;
specs.disabled = false;
})
})
}
6. Улучшите детали: свяжите текст подсказки выше
Мы видим достигнутый эффект.Когда мы нажимаем, текст подсказки выше будет иметь пожалуйста, выберите xxx.После выбора будет текстовый эффект выбранного xxx.Код будет отображаться ниже.
getSelectedInfo() {
let selectTips = '';
if(this.data.selected.length === this.data.allSpecsList.length) {
let selectedText = []
this.data.allSpecsList.forEach(rowSpecs => {
rowSpecs.value_list.forEach(specs => {
if(specs.selected) {
selectedText.push(specs.value)
}
})
})
selectTips = '已选:' + selectedText.join(',');
const selectedItem = this.data.sku.getSelectable(selectedText.join('·'));
this.setData({
selectTips,
selectedItem
})
return
}
const selectedRow = this.data.selected.map(item => item.x);
const unSelected = []
this.data.allSpecsList.forEach((item, index) => {
if(!selectedRow.includes(index)) {
unSelected.push(item.key)
}
})
selectTips = '请选择:' + unSelected.join(',');
this.setData({
selectTips
})
}
Основная идея состоит в том, чтобы проверить, совпадает ли номер в выбранном списке с количеством типов в списке спецификаций.Если они совпадают, то выбираются все, и тогда по этим выбранным спецификациям можно определить SKU, и мы можем получить инвентарь этого SKU Цена, изображение и другая информация, здесь мы сохраняем этот SKU, добавляем отображение библиотеки и т. д., вы можете напрямую добавлять соответствующие данные на страницу.
Если количество выбранных атрибутов не соответствует количеству типов спецификации, узнайте, какие данные спецификации не выбраны, и отобразите их вверху.
резюме
Текущая реализация алгоритма SKU может и не самая лучшая, но я не думаю, что она сложная для понимания. Лучший эффект - посмотреть на идею, а потом реализовать ее самостоятельно. Может показаться простым, но будет много неожиданные трудности при выполнении.
Наконец, поясняется, что эта статья в основном предназначена для изучения МООК.7七月
Преподаватель задает домашнее задание выполнять и думать самостоятельно, а все идеи и коды тоже весь день продумывать и дорабатывать самому.Если есть маленькие партнеры, которые хотят присоединиться к росту вместе, они могут подать заявку на МООК7七月
учительскийОт бэкэнда Java до полного курса стекаобучения.