Home
/
JOYOR S5 Pro 10-Inch Electric Scooter | 500W Motor, 48V 26Ah Battery | 20Km/h Max Speed, 70-100Km Range | ABE Certified, Cruise Control, Color Display, Dual Disc Brakes
${Array(data.total || 0).fill().map((val, idx) => idx).map((val, idx) => `
`).join('')}
1/6
${data.index + 1}/${data.total}
${Array(data.total || 0).fill().map((val, idx) => idx).map((val, idx) => `
`).join('')}
1/6
${data.index + 1}/${data.total}
📢 Follow our official YouTube channel!
This is the JOYOR brand channel, providing:
🎥 Detailed tutorial | 🚴 After-sales support | 🔧 Installation guide | 🌟 Celebrity real review
Click to subscribe to get more exciting content and start your cycling journey! 🚨
👉 Follow us on YouTube
📢 Join the JOYOR official Facebook group!
Here, you will enjoy:
✅ Detailed tutorials
✅ Gifts for events and holidays
✅ Sharing of real riding pictures by customers
Interact with cycling enthusiasts around the world, share your JOYOR cycling stories, and unlock more exciting content! ✨
👉 Click to follow the Facebook group
JOYOR S5 PRO ABE ELECTRIC SCOOTER 2024 NEW
${function() {
return `
0 ? (data.index - 1) : (data.total - 1)},animate=true,direct=-1);"
data-type="prev">
`
}()}
Powerful Motor Conquer Hills
With a 500W Motor Power, the Electric Scooter Joyor S5 Takes you to New Heights, Tackling Terrains with up to a 20° Incline
${function() {
return `
0 ? (data.index - 1) : (data.total - 1)},animate=true,direct=-1);"
data-type="prev">
`
}()}
Total Comfort: Double Front and Rear Suspension
The Suspension System Features a Dual Swingarm Design for both the Front and Rear Wheels, Along with High-Performance 10"x3" Pneumatic Tires Ensuring a Smooth Ride on All Types of Terrain.
${function() {
return `
0 ? (data.index - 1) : (data.total - 1)},animate=true,direct=-1);"
data-type="prev">
`
}()}
Limitless Exploration Awaits
Embark on limitless journeys with the Joyor S5pro 48V 26Ah rechargeable lithium battery, providing an expansive range of 70-100km. Whether for daily commuting or leisurely rides, this electric scooter invites you to discover boundless exploration possibilities.
${function() {
return `
0 ? (data.index - 1) : (data.total - 1)},animate=true,direct=-1);"
data-type="prev">
`
}()}
Efficient Braking:
Front and Rear Disc Brakes
Experience precise braking performance with the advanced technology of front and rear disc brakes. This system provides consistent and reliable stopping power, enhancing safety and providing you with the peace of mind to enjoy your Joyor S5pro ride to the fullest.
${function() {
return `
0 ? (data.index - 1) : (data.total - 1)},animate=true,direct=-1);"
data-type="prev">
`
}()}
All-Around Visibility
Stand out and stay visible with Joyor's integrated front and rear lights. Enjoy safe rides even in low-light conditions, enhancing your visibility and overall riding experience.
Advanced control: Multifunctional LCD screen
Stay in command of your Joyor S5pro ride with the multifunctional LCD display. Access various settings and enhance your riding experience with ultimate control and convenience. Includes three driving modes and a push mode for added flexibility.
${function() {
return `
0 ? (data.index - 1) : (data.total - 1)},animate=true,direct=-1);"
data-type="prev">
`
}()}
Redefining Durability
Experience the Joyor S5's captivating design – an aluminum alloy frame with sleek modern lines and an eye-catching orange swingarm. Engineered for all-terrain versatility, this scooter radiates stability, promising to be your unwavering companion on every adventure.
${function() {
return `
0 ? (data.index - 1) : (data.total - 1)},animate=true,direct=-1);"
data-type="prev">
`
}()}
Electric scooter approved by the German ABE
The Joyor S5PRO electric scooter has received official ABE (Allgemeine Betriebserlaubnis) certification from Germany. This certification ensures that the S5PRO meets all safety and regulatory standards set by the German government.
${function() {
return `
0 ? (data.index - 1) : (data.total - 1)},animate=true,direct=-1);"
data-type="prev">
`
}()}
(function() {
const STATUS = {
LOADING: 'loading',
FINISH: 'finish'
};
const RESULT = {
EMPTY: 'data-empty'
};
class SPZCustomCartSku extends SPZ.BaseElement {
renderData = [];
/**
* add to cart reselect item, and delete process:
* 1. record reselect id before show reselect modal
* 2. add to cart success, mark `needDeleteReselectRecord` to true
* 3. close reselect modal / drawer
* 4. spz-cart re-render, triggered mounted event
* 5. call refresh
*/
// mark delete reselect id
recordReselectId = null;
// mark should delete reselect record after spz-cart mounted event
needDeleteReselectRecord = false;
refreshLock = false;
addToCartSuccess = false;
// cache paused refresh data
refreshDataCache = [];
// cache similar products data, for manual add to cart
similarData = [];
constructor(element) {
super(element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.action_ = SPZServices.actionServiceForDoc(this.element);
}
setupAction_() {
this.registerAction('refresh', (invocation) => {
const data = invocation && invocation.args && invocation.args.data || {};
this.refresh(data);
});
this.registerAction('deleteReselect', SPZCore.Types.debounce(
this.win,
(invocation) => {
this.deleteReselect(invocation?.args?.id);
},
200
));
this.registerAction('deleteInvalid', SPZCore.Types.debounce(
this.win,
(invocation) => {
this.deleteInvalid(invocation?.args?.id);
},
200
));
this.registerAction('setReselectRecordId', (invocation) => {
this.setReselectRecordId(invocation?.args?.id);
});
this.registerAction('clearNeedReselectRecord', (invocation) => {
this.clearNeedReselectRecord();
});
this.registerAction('registerDeleteReselectTask', (invocation) => {
this.registerDeleteReselectTask(invocation?.args?.data);
});
this.registerAction('lockRefresh', (invocation) => {
this.lockRefresh();
});
this.registerAction('releaseRefreshLock', (invocation) => {
this.releaseRefreshLock();
});
this.registerAction('manualAddToCart', (invocation) => {
this.manualAddToCart(invocation?.args?.id);
});
this.registerAction('syncSimilarData', (invocation) => {
this.syncSimilarData(invocation?.args?.data);
});
// DEBUG
this.registerAction('log', (invocation) => {
const data = invocation && invocation.args && invocation.args.data || {};
console.log('log', invocation);
});
}
buildCallback() {
this.setupAction_();
}
isLayoutSupported(layout) {
return true;
}
/**
* 数据去重
* @param {Array} data
*/
uniq_(data) {
const result = [];
for(let i = 0; i < data.length; i++) {
if(!result.includes(data[i])) {
result.push(data[i]);
}
}
return result;
}
checkRefreshLock() {
if (this.refreshLock) {
return false;
}
return true;
}
setLoading(isLoading) {
SPZCore.Dom.toggleAttribute(this.element, STATUS.LOADING, isLoading);
SPZCore.Dom.toggleAttribute(this.element, STATUS.FINISH, !isLoading);
}
setDataResult(data) {
const isDataEmpty = !data.reselectSkus.length && !data.invalidSkus.length && !data.line_items.length;
SPZCore.Dom.toggleAttribute(this.element, RESULT.EMPTY, isDataEmpty);
}
// 存在多次请求顺序问题
async refresh(_data) {
// 接口失败时返回数据不为对象
if (!(typeof _data === 'object' && _data.line_items && _data.line_items.length > 0)) {
const newData = {
line_items: [],
reselectSkus: [],
invalidSkus: [],
displayInvalidSkus: []
};
this.trigger_('refreshError', newData);
this.setLoading(false);
return;
}
if (!this.checkRefreshLock()) {
this.setRefreshCache(_data);
return;
}
this.setLoading(true);
let data = _data;
if (this.needDeleteReselectRecord && this.recordReselectId) {
let hasError = false;
try {
data = await this.deleteReselectRecordBeforeRefresh(_data);
} catch (e) {
hasError = true;
console.error(e);
const newData = {
...data,
reselectSkus: [],
invalidSkus: [],
displayInvalidSkus: []
};
this.trigger_('refreshError', newData);
this.setLoading(false);
this.setDataResult(newData);
return;
}
this.needDeleteReselectRecord = false;
if (hasError) {
return;
}
}
// 获取失效商品
const invisibleItems = data.ineffectives;
// 获取失效商品内仅售罄商品的sku
const soldOutItems = invisibleItems.filter(item => item.reason === "line_item_sold_out");
// 通过失效 sku 获取 spu, 注意去重
const soldOutSpuIds = this.uniq_(soldOutItems.map(item => item.product_id));
const querySpuIds = soldOutSpuIds.map(spuId => `ids[]=${spuId}`).join('&');
if (!soldOutSpuIds.length) {
const newData = {
...data,
reselectSkus: [],
invalidSkus: [],
displayInvalidSkus: []
};
this.trigger_('refreshSuccess', newData);
this.renderData = newData;
this.setLoading(false);
this.setDataResult(newData);
return;
}
// 请求 spu 下其他 sku 库存
this.xhr_.fetchJson(`/api/product/list?limit=${soldOutSpuIds.length}&${querySpuIds}`)
.then(res => {
const reselectSkus = [];
const invalidSkus = [];
// spu 维度展示
const displayInvalidSkus = [];
res.data.list.forEach(product => {
// spu 匹配, 存在多 sku 情况
const soldOutSkus = soldOutItems.filter(item => item.product_id === product.id);
if (!soldOutSkus.length) {
return;
}
// 通过失效 sku 获取 spu 下其他 sku 库存, 存在其他可用库存时标记为 "Reselect" 按钮可用
//const allowReselect = product.variants
// .filter(variant => variant.option1 === soldOutProduct.variant.option1 && variant.option2 === soldOutProduct.variant.option2 && variant.option3 === soldOutProduct.variant.option3)
// .some(variant => variant.available);
// 查询售罄 sku 对应商品是否可加购, 标记为 Reselect 可用项
if (product.available) {
reselectSkus.push(...soldOutSkus);
// 若商品不可用(全部 sku 售罄), 归纳到失效商品列表
} else {
invalidSkus.push(...soldOutSkus);
// spu 维度, 仅添加一项 sku
displayInvalidSkus.push(soldOutSkus[0]);
}
});
const newData = {
...data,
reselectSkus,
invalidSkus,
displayInvalidSkus
};
this.trigger_('refreshSuccess', newData);
this.renderData = newData;
this.setLoading(false);
this.setDataResult(newData);
})
.catch(err => {
this.setLoading(false);
console.error(err);
this.trigger_('refreshError', data);
}).finally(() => {
});
}
async deleteReselect(id, ignoreEmit) {
if (!id) {
return;
}
const targetSku = this.renderData.reselectSkus.find(item => item.id === id);
try {
const res = await this.xhr_.fetchJson(`/api/cart/${targetSku.variant_id}`, {
method: 'DELETE',
body: {
id: targetSku.id,
product_id: targetSku.product_id,
variant_id: targetSku.variant_id,
}
});
const newData = {
...this.renderData,
reselectSkus: this.renderData.reselectSkus.filter(item => item.id !== id),
};
this.renderData = newData;
!ignoreEmit && this.trigger_('deleteSuccess', newData);
} catch (err) {
console.error(err);
!ignoreEmit && this.trigger_('deleteError', err);
}
}
deleteInvalid(id) {
if (!id) {
return;
}
const targetSku = this.renderData.invalidSkus.find(item => item.id === id);
this.xhr_.fetchJson(`/api/cart/${targetSku.variant_id}`, {
method: 'DELETE',
body: {
id: targetSku.id,
product_id: targetSku.product_id,
variant_id: targetSku.variant_id,
}
})
.then(res => {
const newData = {
...this.renderData,
invalidSkus: this.renderData.invalidSkus.filter(item => item.id !== id),
displayInvalidSkus: this.renderData.displayInvalidSkus.filter(item => item.id !== id),
};
this.trigger_('deleteSuccess', newData);
this.renderData = newData;
})
.catch(err => {
console.error(err);
this.trigger_('deleteError', err);
});
}
// record reselect sku id before show reselect modal
setReselectRecordId(id) {
if (!id) {
return;
}
this.recordReselectId = id;
}
async deleteReselectRecordBeforeRefresh(data) {
if (!this.recordReselectId) {
return;
}
await this.deleteReselect(this.recordReselectId, true);
return {
...data,
ineffectives: data.ineffectives.filter(item => item.id !== this.recordReselectId)
}
}
clearNeedReselectRecord() {
this.needDeleteReselectRecord = false;
}
registerDeleteReselectTask(productData) {
const targetSku = this.renderData.reselectSkus.find(item => item.id === this.recordReselectId);
if (targetSku?.variant_id && productData?.variant.id) {
if (targetSku.variant_id === productData?.variant.id) {
this.needDeleteReselectRecord = false;
return;
}
}
this.needDeleteReselectRecord = true;
}
// pause cart refresh(trigger by similar products)
lockRefresh() {
if (this.refreshLock) {
return;
}
this.refreshLock = true;
}
releaseRefreshLock() {
if (!this.refreshLock) {
return;
}
this.refreshLock = false;
this.refreshWithCache();
}
// direct add_to_cart(trigger by similar products)
async manualAddToCart(id) {
const target = this.similarData.find(item => item.id === id);
this.lockRefresh();
try {
const res = await this.xhr_.fetchJson(`/api/cart`, {
method: 'POST',
body: {
note: '',
product_id: target.id,
quantity: '1',
variant_id: target.variants[0].id,
refer_info: {
source: 'add_to_cart'
}
}
});
const newCartItems = res.data.items;
this.setRefreshCache(newCartItems, true);
} catch (err) {
console.error(err);
}
}
syncSimilarData(data) {
this.similarData = data.data;
}
setRefreshCache(data, patch) {
if (patch) {
this.refreshDataCache = {
...this.refreshDataCache,
line_items: [...this.refreshDataCache.line_items, ...data]
};
} else {
this.refreshDataCache = data;
}
}
refreshWithCache() {
this.refresh(this.refreshDataCache);
}
mountCallback() {
}
unmountCallback() {
}
/**
* trigger event
* @param {Object} data
*/
trigger_(name, data) {
const event = SPZUtils.Event.create(this.win, `spz-custom-cart-sku.${name}`, {
data,
});
this.action_.trigger(this.element, name, event);
}
}
SPZ.defineElement('spz-custom-cart-sku', SPZCustomCartSku);
}())
(function () {
class SPZCustomCartTrack extends SPZ.BaseElement {
constructor(element) {
super(element);
this.action_ = SPZServices.actionServiceForDoc(this.element);
}
isLayoutSupported(layout) {
return true;
}
buildCallback() {
this.setupAction_();
}
hash(val) {
const hashKey = Object.keys(val).sort((a, b) => a - b).reduce((acc, k) => {
acc += `{'${k}':'${val[k]}'}`;
return acc;
}, '');
return hashKey;
}
trackExtra(key, value) {
console.log('trackExtra...', key, value);
if (!window.sa) {
return;
}
const hashKey = this.hash(value);
let hasExtraInfo = false;
if (window.sa.eventInfo && window.sa.eventInfo[key] && window.sa.eventInfo[key].extra_properties) {
hasExtraInfo = window.sa.eventInfo[key].extra_properties.some(p => {
return hashKey === this.hash(p);
});
}
if (hasExtraInfo) {
return;
}
window.sa && window.sa.registerExtraInfo(key, value);
}
delExtraTrack(key, value) {
const hashKey = this.hash(value);
if (window.sa.eventInfo && window.sa.eventInfo[key] && window.sa.eventInfo[key].extra_properties) {
window.sa.eventInfo[key].extra_properties = window.sa.eventInfo[key].extra_properties.filter(p => hashKey !== this.hash(p));
}
}
track(key, value) {
console.log('tracking...', key, value);
window.sa && window.sa.track(key, value);
}
setupAction_() {
this.registerAction('registerReselectAtc', () => {
this.trackExtra('add_to_cart', {
function_name: 'Farida',
action_type: 'reselect'
});
});
this.registerAction('clearReselectAtc', () => {
this.delExtraTrack('add_to_cart', {
function_name: 'Farida',
action_type: 'reselect'
});
});
this.registerAction('registerSimilarAtc', () => {
this.trackExtra('add_to_cart', {
function_name: 'Farida',
action_type: 'similar_product'
});
});
this.registerAction('clearSimilarAtc', () => {
this.delExtraTrack('add_to_cart', {
function_name: 'Farida',
action_type: 'similar_product'
});
});
const clickParams = {
business_type: 'product_plugin',
event_name: 'function_click',
function_name: 'Farida',
plugin_name: 'Farida',
template_name: 'cart',
template_type: '13',
module: 'online_store',
module_type: 'online_store',
tab_name: '',
card_name: '',
event_developer: 'ccbken',
event_type: 'click',
};
this.registerAction('trackDelReselect', () => {
this.track('function_click', {
...clickParams,
event_info: JSON.stringify({
action_type: 'cart_delete',
element_type: 'sku'
})
});
});
this.registerAction('trackClickReselect', () => {
this.track('function_click', {
...clickParams,
event_info: JSON.stringify({
action_type: 'reselect',
element_type: 'sku'
})
});
});
this.registerAction('trackDelSimilar', () => {
this.track('function_click', {
...clickParams,
event_info: JSON.stringify({
action_type: 'cart_delete',
element_type: 'spu'
})
});
});
this.registerAction('trackClickSimilar', () => {
this.track('function_click', {
...clickParams,
event_info: JSON.stringify({
action_type: 'reselect',
element_type: 'spu'
})
});
});
this.registerAction('trackOpenSimilar', () => {
this.track('function_expose', {
...clickParams,
event_name: 'function_expose',
event_type: 'expose',
event_info: JSON.stringify({
popup_name: 'farida_product_popup'
})
});
});
}
}
SPZ.defineElement('spz-custom-cart-track', SPZCustomCartTrack);
}())