update resources\web

This commit is contained in:
wjyLearn
2025-12-20 17:45:44 +08:00
parent 328f91afc2
commit 69be99b00a
264 changed files with 7342 additions and 84 deletions

View File

@@ -0,0 +1,174 @@
const {
ClassicEditor,
Autosave,
Essentials,
Paragraph,
ImageUtils,
ImageEditing,
Link,
List,
Alignment,
Bold,
Italic,
Underline,
AutoLink,
Heading
} = window.CKEDITOR;
const LICENSE_KEY =
'GPL';
const CKEDITOR_LANGUAGE_MAP = {
en: 'en',
zh_CN: 'zh-cn',
ja_JP: 'ja',
it_IT: 'it',
fr_FR: 'fr',
de_DE: 'de',
hu_HU: 'hu',
es_ES: 'es',
sv_SE: 'sv',
cs_CZ: 'cs',
nl_NL: 'nl',
uk_UA: 'uk',
ru_RU: 'ru',
tr_TR: 'tr',
pt_BR: 'pt-br',
ko_KR: 'ko',
pl_PL: 'pl'
};
function detectEditorLanguage() {
let lang = typeof GetQueryString === 'function' ? GetQueryString('lang') : null;
const langStorageKey = typeof LANG_COOKIE_NAME !== 'undefined' ? LANG_COOKIE_NAME : 'QIDIWebLang';
try {
if (lang && typeof localStorage !== 'undefined') {
localStorage.setItem(langStorageKey, lang);
}
} catch (err) {}
if (!lang) {
try {
if (typeof localStorage !== 'undefined') {
lang = localStorage.getItem(langStorageKey);
}
} catch (err) {
lang = null;
}
}
if (!lang && typeof navigator !== 'undefined') {
lang = navigator.language || navigator.userLanguage;
}
if (!lang) {
return 'en';
}
const normalizedUnderscore = lang.replace('-', '_');
if (CKEDITOR_LANGUAGE_MAP[normalizedUnderscore]) {
return CKEDITOR_LANGUAGE_MAP[normalizedUnderscore];
}
if (CKEDITOR_LANGUAGE_MAP[lang]) {
return CKEDITOR_LANGUAGE_MAP[lang];
}
return normalizedUnderscore.replace('_', '-').toLowerCase();
}
const editorLanguage = detectEditorLanguage();
const script = document.createElement('script');
script.src = `../include/ckeditor5/translations/${editorLanguage}.umd.js`;
const editorConfig = {
toolbar: {
items: [
'heading',
'|',
'bold',
'italic',
'underline',
'|',
'alignment',
'|',
'bulletedList',
'numberedList',
'|',
'link',
'|',
'undo',
'redo',
],
shouldNotGroupWhenFull: false
},
plugins: [Alignment, AutoLink, Autosave, Bold, Essentials, Heading, ImageEditing, ImageUtils, Italic, Link, List, Paragraph, Underline],
heading: {
options: [
{
model: 'paragraph',
title: 'Paragraph',
class: 'ck-heading_paragraph'
},
{
model: 'heading1',
view: 'h1',
title: 'Heading 1',
class: 'ck-heading_heading1'
},
{
model: 'heading2',
view: 'h2',
title: 'Heading 2',
class: 'ck-heading_heading2'
},
{
model: 'heading3',
view: 'h3',
title: 'Heading 3',
class: 'ck-heading_heading3'
},
{
model: 'heading4',
view: 'h4',
title: 'Heading 4',
class: 'ck-heading_heading4'
},
{
model: 'heading5',
view: 'h5',
title: 'Heading 5',
class: 'ck-heading_heading5'
},
{
model: 'heading6',
view: 'h6',
title: 'Heading 6',
class: 'ck-heading_heading6'
}
]
},
initialData:
'',
licenseKey: LICENSE_KEY,
link: {
addTargetToExternalLinks: true,
defaultProtocol: 'https://',
decorators: {
toggleDownloadable: {
mode: 'manual',
label: 'Downloadable',
attributes: {
download: 'file'
}
}
}
},
placeholder: '',
language: editorLanguage
};
script.onload = () => {
ClassicEditor.create(document.querySelector('#editor'), editorConfig).then( editor => {
window.projectEditor = editor;
});
ClassicEditor.create(document.querySelector('#profile-editor'), editorConfig).then( editor => {
window.profileEditor = editor;
});
};
document.head.appendChild(script);

View File

@@ -0,0 +1,742 @@
var projectName = "";
var projectPictures = [
// {
// "filepath": "",
// "filename": "",
// "size": 0
// }
];
var projectEditorData;
var bomAccessories = []; //like projectPictures structure
var assemblyAccessories = []; //like projectPictures structure
var otherAccessories = []; //like projectPictures structure
var profileName = "";
var profilePictures = [];
var profileEditorData;
var bakRequestData;
function deepCloneList(list) {
return JSON.parse(JSON.stringify(list || []));
}
function safeDecode(value) {
if (!value) return '';
try {
return decodeURIComponent(value);
} catch (err) {
return value;
}
}
function getBase64ImageFormat(base64Str) {
if (!isBase64String(base64Str)) return null;
const trimmed = base64Str.trim();
const dataUrlMatch = trimmed.match(/^data:(.*?);base64,/i);
let payload = trimmed;
if (dataUrlMatch) {
const mime = (dataUrlMatch[1] || '').toLowerCase();
payload = trimmed.slice(dataUrlMatch[0].length);
if (mime.indexOf('image/png') >= 0) return 'png';
if (mime.indexOf('image/jpeg') >= 0 || mime.indexOf('image/jpg') >= 0) return 'jpg';
}
const cleanPayload = payload.replace(/\s+/g, '');
try {
const header = atob(cleanPayload.slice(0, 16));
const bytes = header.split('').map(ch => ch.charCodeAt(0));
if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47) return 'png';
if (bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF) return 'jpg';
} catch (err) {
return null;
}
return null;
}
function normalizeCoverName(value) {
const decoded = safeDecode(value);
if (!decoded) return '';
const segments = decoded.split(/[/\\]/);
return segments[segments.length - 1];
}
function normalizeRequestPayload(raw) {
const safe = raw || {};
const model = safe.model || {};
const file = safe.file || {};
const profile = safe.profile || {};
return {
model: {
name: model.name || "",
description: model.description || "",
preview_img: deepCloneList(model.preview_img),
},
file: {
BOM: deepCloneList(file.BOM),
Assembly: deepCloneList(file.Assembly),
Other: deepCloneList(file.Other),
},
profile: {
name: profile.name || "",
description: profile.description || "",
preview_img: deepCloneList(profile.preview_img),
},
};
}
function getCurrentRequestData() {
getProjectName();
getProjectDescription();
getProfileName();
getProfilePictures();
getProfileDescription();
return {
model: {
name: encodeURIComponent(projectName || ""),
description: encodeURIComponent(projectEditorData || ""),
preview_img: deepCloneList(projectPictures),
},
file: {
BOM: deepCloneList(bomAccessories),
Assembly: deepCloneList(assemblyAccessories),
Other: deepCloneList(otherAccessories),
},
profile: {
name: encodeURIComponent(profileName || ""),
description: encodeURIComponent(profileEditorData || ""),
preview_img: deepCloneList(profilePictures),
},
};
}
const editorUploadRequests = new Map();
$(function () {
TranslatePage();
addAccessoryBtnListener();
addPictureUploadListener(
'projectImageInput',
'imageList',
projectPictures,
['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/jpg'],
4 * 1024 * 1024
);
addAccessoryUploadListener(
'bom-input',
'bom-list',
bomAccessories,
{
'.xls': 10 * 1024 * 1024,
'.xlsx': 10 * 1024 * 1024,
'.pdf': 50 * 1024 * 1024
},
10
);
addAccessoryUploadListener(
'assembly-guide-input',
'assembly-list',
assemblyAccessories,
{
'.jpg': 10 * 1024 * 1024,
'.png': 10 * 1024 * 1024,
'.pdf': 50 * 1024 * 1024
},
25
);
addAccessoryUploadListener(
'other-input',
'other-list',
otherAccessories,
{
'.txt': 10 * 1024 * 1024,
},
10
);
addPictureUploadListener(
'ProfileImageInput',
'profileImageList',
profilePictures,
['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/jpg'],
4 * 1024 * 1024
);
// TestProjectData.model.preview_img=[];
// updateInfo(TestProjectData);
RequestProjectInfo();
});
function isFormEmpty() {
getProjectName();
getProjectDescription();
getProfileName();
getProfilePictures();
getProfileDescription();
const noBasics = !projectName.trim() && !projectEditorData?.trim();
const noProjectPics = projectPictures.length === 0;
const noAttachments = bomAccessories.length === 0 &&
assemblyAccessories.length === 0 &&
otherAccessories.length === 0;
const noProfileInfo = !profileName.trim() &&
!profileEditorData?.trim() &&
profilePictures.length === 0;
return noBasics && noProjectPics && noAttachments && noProfileInfo;
}
function isChange() {
if (!bakRequestData && isFormEmpty()) {
return false;
}
const baseline = normalizeRequestPayload(bakRequestData);
const current = normalizeRequestPayload(getCurrentRequestData());
return JSON.stringify(baseline) !== JSON.stringify(current);
}
function saveInfo() {
let modelData = {};
getProjectName();
if (!projectName) {
showToast("The project name is empty.");
return;
}
modelData["name"] = encodeURIComponent(projectName);
if (projectPictures.length <= 0) {
showToast("The project pictures is empty.");
return;
}
modelData["preview_img"] = projectPictures;
getProjectDescription();
if (!projectEditorData) {
showToast("The project description is empty.");
return;
}
modelData["description"] = encodeURIComponent(projectEditorData);
let fileData = {
BOM: bomAccessories,
Assembly: assemblyAccessories,
Other: otherAccessories
};
getProfileName();
getProfilePictures();
getProfileDescription();
let profileData = {
name: encodeURIComponent(profileName),
preview_img: profilePictures,
description: encodeURIComponent(profileEditorData)
};
let updateData = {
model: modelData,
file: fileData,
profile: profileData
};
var tSend={};
tSend['sequence_id']=Math.round(new Date() / 1000);
tSend['command']="update_3mf_info";
tSend['model'] = updateData;
SendWXMessage( JSON.stringify(tSend) );
}
function updateInfo(p3MF) {
projectName = DOMPurify.sanitize(decodeURIComponent(p3MF.model.name));
projectPictures.length = 0;
Array.prototype.push.apply(projectPictures, p3MF.model.preview_img || []);
let projectCover = normalizeCoverName(p3MF.model.cover_img || "");
for (let i = 0; i < projectPictures.length; i++) {
const pictureName = normalizeCoverName(projectPictures[i].filename);
if (projectCover && pictureName === projectCover){
const [item] = projectPictures.splice(i, 1);
projectPictures.unshift(item);
break;
}
}
projectEditorData = decodeURIComponent(p3MF.model.description) || '';
bomAccessories.length = 0;
Array.prototype.push.apply(bomAccessories, p3MF.file.BOM || []);
assemblyAccessories.length = 0;
Array.prototype.push.apply(assemblyAccessories, p3MF.file.Assembly || []);
otherAccessories.length = 0;
Array.prototype.push.apply(otherAccessories, p3MF.file.Other || []);
profileName = DOMPurify.sanitize(decodeURIComponent(p3MF.profile.name));
profilePictures.length = 0;
Array.prototype.push.apply(profilePictures, p3MF.profile.preview_img || []);
let profileCover = normalizeCoverName(p3MF.profile.cover_img || "");
for (let i = 0; i < profilePictures.length; i++) {
const pictureName = normalizeCoverName(profilePictures[i].filename);
if (profileCover && pictureName === profileCover){
const [item] = profilePictures.splice(i, 1);
profilePictures.unshift(item);
break;
}
}
profileEditorData = decodeURIComponent(p3MF.profile.description) || '';
setProjectName();
setProjectPictrues();
setProjectDescription();
setAccessor();
setProfileName();
setProfilePictures();
setProfileDescription();
}
function setProjectName() {
$("#projectNameInput").val(projectName);
}
function getProjectName() {
projectName = $("#projectNameInput").val();
}
function setProjectPictrues() {
setPictures('imageList', projectPictures);
}
function getProjectPictures() {
return projectPictures;
}
function setProjectDescription() {
if (window.projectEditor) {
projectEditor.setData(projectEditorData || '');
}
}
function getProjectDescription() {
if (window.projectEditor) {
projectEditorData = projectEditor.getData();
}
}
function setAccessor() {
setAccessories('bom-list', bomAccessories);
setAccessories('assembly-list', assemblyAccessories);
setAccessories('other-list', otherAccessories);
}
function setProfileName() {
$("#ProfileNameInput").val(profileName);
}
function getProfileName() {
profileName = $("#ProfileNameInput").val();
}
function setProfilePictures() {
setPictures('profileImageList', profilePictures);
}
function getProfilePictures() {
return profilePictures;
}
function setProfileDescription() {
if (window.profileEditor) {
profileEditor.setData(profileEditorData || '');
}
}
function getProfileDescription() {
if (window.profileEditor) {
profileEditorData = profileEditor.getData();
}
}
//Pictures tool
function setPictures(id, picturesList) {
let updateHtml = "";
for (let i = 0; i < picturesList.length; i++) {
let pic_filepath = picturesList[i].filepath;
if(picturesList[i].base64) {
pic_filepath = picturesList[i].base64;
}
if (i == 0) {
let html = `<div class="imagePreview" data-index="${i}" style="background-image: url('${pic_filepath}')">
<img src="img/img_del.svg" />
<div class="modelCover">Model cover</div>
</div>`;
updateHtml = html + updateHtml;
}else {
let html = `<div class="imagePreview" data-index="${i}" style="background-image: url('${pic_filepath}')">
<img src="img/img_del.svg" />
<div class="setModelCover">Set as cover</div>
</div>`;
updateHtml += html;
}
}
$(`#${id}`).html(updateHtml);
$(`#${id}`).off('click', 'img');
$(`#${id}`).on('click', 'img', function (event) {
let index = parseInt($(this).parent().data('index'));
removePictureAt(index, picturesList);
setPictures(id, picturesList);
});
$(`#${id}`).off('click', '.setModelCover');
$(`#${id}`).on('click', '.setModelCover', function (event) {
let index = parseInt($(this).parent().data('index'));
const [item] = picturesList.splice(index, 1);
picturesList.unshift(item);
setPictures(id, picturesList);
});
}
function removePictureAt(index, picturesList) {
if (index < 0 || index >= picturesList.length) {
return;
}
picturesList.splice(index, 1);
}
function addPictureUploadListener(inputId, showPictrueId, picturesList, allowTypes, maxSize, maxCount) {
const input = document.getElementById(inputId);
// input.removeEventListener('click');
input.addEventListener('click', () => {
input.value = '';
});
// input.removeEventListener('change');
input.addEventListener('change', function (event) {
const [file] = event.target.files;
if (!file) return;
// Validate file type
if (allowTypes && !allowTypes.includes(file.type)) {
showToast(GetCurrentTextByKey("t144"));
return;
}
// Validate file size
if (maxSize && file.size > maxSize) {
showToast(GetCurrentTextByKey("t145"));
return;
}
if (picturesList.length >= maxCount) {
showToast(GetCurrentTextByKey("t146"));
return;
}
uploadFileToCpp(file).then(result => {
if (result && result.path) {
fileToThumbnailBase64(file).then((base64) => {
picturesList.push({
"filepath": result.path,
"filename": file.name,
"size": file.size,
"base64": base64
});
setPictures(showPictrueId, picturesList);
// showToast('add file1'+projectPictures.length);
});
}
}).catch(error => {
showToast(GetCurrentTextByKey("t147"));
});
});
}
//accessories tool
function addAccessoryBtnListener() {
$("#accessories-btn").on("click", function (e) {
e.stopPropagation();
$(".accessory-rule-wrapper").toggle();
});
$(document).on('click', function (e) {
if (!$(e.target).closest('#accessories-btn, .accessory-rule-wrapper').length) {
$('.accessory-rule-wrapper').hide();
}
});1
}
function setAccessories(id, accessoriesList) {
let updateHtml = "";
if (accessoriesList.length > 0) {
$(`#${id}`).prev().show();
}else {
$(`#${id}`).prev().hide();
}
for (let i = 0; i < accessoriesList.length; i++) {
let acc_filepath = accessoriesList[i].filepath;
let type = "";
if (accessoriesList[i].type) {
type = getFileTail(accessoriesList[i].type);
}else {
type = getFileType(acc_filepath);
}
let iconPath = `img/icon_${type}.svg`;
let html = `<div class="attachment" data-index="${i}"><img class="attachment-icon" src="${iconPath}">${decodeURIComponent(accessoriesList[i].filename)}<img class="attachment-delete" src="img/del.svg"></div>`;
updateHtml += html;
}
$(`#${id}`).html(updateHtml);
$(`#${id}`).children('.attachment').each(function(idx) {
$(this).data('path', accessoriesList[idx]?.filepath || '');
});
$(`#${id}`).prev().children('label').text(accessoriesList.length);
$(`#${id}`).off('click', '.attachment-delete');
$(`#${id}`).on('click', '.attachment-delete', function (event) {
event.stopPropagation();
let index = parseInt($(this).parent().data('index'));
removeAccessoryAt(index, accessoriesList);
setAccessories(id, accessoriesList);
});
$(`#${id}`).off('click', '.attachment');
$(`#${id}`).on('click', '.attachment', function (event) {
if ($(event.target).closest('.attachment-delete').length) return;
const path = $(this).data('path');
OnClickOpenFile(event, path);
});
}
function removeAccessoryAt(index, accessoriesList) {
if (index < 0 || index >= accessoriesList.length) {
return;
}
accessoriesList.splice(index, 1);
}
function getFileTail(filetype) {
switch(filetype) {
case 'application/pdf':
return 'pdf';
case 'text/plain':
return 'txt';
case 'image/jpeg':
return 'jpg';
case 'image/png':
return 'png';
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
return 'xcl';
case 'application/vnd.ms-excel':
return 'xcl';
default:
return '';
}
}
function getFileType(filepath) {
let parts = filepath.split('.');
if (parts.length > 1) {
switch (parts[parts.length - 1].toLowerCase()) {
case 'pdf':
return 'pdf';
case 'txt':
return 'txt';
case 'jpg':
return 'jpg';
case 'jpet':
return 'jpg';
case 'png':
return 'png';
case 'xlsx':
case 'xls':
return 'xcl';
}
}
return getBase64ImageFormat(filepath);
}
function addAccessoryUploadListener(inputId, showAccessoryId, accessoriesList, allowTypes, maxCount) {
const input = document.getElementById(inputId);
input.addEventListener('click', () => {
input.value = '';
});
input.addEventListener('change', function (event) {
const [file] = event.target.files;
if (!file) return;
const lowerName = file.name.toLowerCase();
const matchedExt = Object.keys(allowTypes).find(ext => lowerName.endsWith(ext));
if (!matchedExt) {
showToast(GetCurrentTextByKey("t144"));
return;
}
const maxSize = allowTypes[matchedExt];
if (maxSize && file.size > maxSize) {
showToast(GetCurrentTextByKey("t145"));
return;
}
if (accessoriesList.length >= maxCount) {
showToast(GetCurrentTextByKey("t146"));
return;
}
uploadFileToCpp(file).then(result => {
if (result && result.path) {
accessoriesList.push({
"filepath": result.path,
"filename": file.name,
"size": file.size,
"type": file.type
});
setAccessories(showAccessoryId, accessoriesList);
}
}).catch(error => {
showToast(GetCurrentTextByKey("t147"));
});
});
}
//common tool
function generateSequenceId() {
return `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
}
function uploadFileToCpp(file) {
return new Promise((resolve, reject) => {
if (!(file instanceof File)) {
reject(new Error('invalid file object'));
return;
}
const reader = new FileReader();
const sequenceId = generateSequenceId();
reader.onload = () => {
let result = reader.result;
let base64Payload = '';
if (typeof result === 'string') {
const commaIndex = result.indexOf(',');
base64Payload = commaIndex >= 0 ? result.substring(commaIndex + 1) : result;
} else if (result instanceof ArrayBuffer) {
const bytes = new Uint8Array(result);
let binary = '';
const chunk = 0x8000;
for (let i = 0; i < bytes.length; i += chunk) {
binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunk));
}
base64Payload = btoa(binary);
} else {
reject(new Error('unable to read file content'));
return;
}
const message = {
sequence_id: sequenceId,
command: 'editor_upload_file',
data: {
filename: file.name || '',
size: file.size || 0,
type: file.type || '',
base64: base64Payload
}
};
editorUploadRequests.set(sequenceId, { resolve, reject });
const canCallHost =
typeof window !== 'undefined' &&
typeof window.wx === 'object' &&
typeof window.wx.postMessage === 'function';
if (typeof SendWXMessage === 'function' && canCallHost) {
try {
SendWXMessage(JSON.stringify(message));
} catch (error) {
editorUploadRequests.delete(sequenceId);
reject(error);
}
} else {
editorUploadRequests.delete(sequenceId);
const fallbackPath = URL.createObjectURL(file);
resolve({
path: fallbackPath,
isMock: true
});
}
};
reader.onerror = () => {
reject(reader.error || new Error('failed to read file'));
};
reader.readAsDataURL(file);
});
}
function fileToThumbnailBase64(file, maxWidth = 200, quality = 0.85) {
return new Promise((resolve, reject) => {
if (!file || !file.type.startsWith('image/')) {
reject(new Error('select a valid image file'));
return;
}
const reader = new FileReader();
reader.onload = e => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = e.target.result;
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const scale = Math.min(1, maxWidth / img.width);
const width = img.width * scale;
const height = img.height * scale;
canvas.width = width;
canvas.height = height;
if (file.type === 'image/png' || file.type === 'image/webp') {
ctx.clearRect(0, 0, width, height);
} else {
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, width, height);
}
ctx.drawImage(img, 0, 0, width, height);
const outputType = ['image/png', 'image/jpeg', 'image/webp'].includes(file.type)
? file.type
: 'image/png';
const base64 = canvas.toDataURL(outputType, quality);
resolve(base64);
};
img.onerror = () => reject(new Error('invalid image file'));
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
function handleEditorMessage(rawMessage) {
let payload = rawMessage;
if (typeof rawMessage === 'string') {
try {
payload = JSON.parse(rawMessage);
} catch (error) {
return;
}
}
if (!payload || typeof payload !== 'object') {
return;
}
const command = payload.command;
if (command === 'editor_upload_file_result') {
const sequenceId = payload.sequence_id;
if (!sequenceId || !editorUploadRequests.has(sequenceId)) {
return;
}
const { resolve, reject } = editorUploadRequests.get(sequenceId);
editorUploadRequests.delete(sequenceId);
if (payload.error) {
reject(new Error(payload.error));
return;
}
resolve(payload.data || {});
return;
}
if (command === 'save_project') {
saveInfo();
return;
}
if (command === 'discard_project') {
window.location.href = 'index.html';
return;
}
if (command === 'update_3mf_info_result') {
if (payload.error) {
showToast(payload.error);
return;
}
bakRequestData = JSON.parse(JSON.stringify(getCurrentRequestData()));
showToast(payload.message || 'Saved successfully.');
window.location.href = "index.html";
}
}
function OnClickOpenFile( evt, strFullPath ) {
if (!strFullPath) return;
if (evt && $(evt.target).closest('.attachment-delete').length) {
return;
}
if(isBase64String(strFullPath)) {
showBase64ImageLayer(strFullPath);
return;
}
var tSend={};
tSend['sequence_id']=Math.round(new Date() / 1000);
tSend['command']="open_3mf_accessory";
tSend['accessory_path']=strFullPath;
SendWXMessage( JSON.stringify(tSend) );
SendWXDebugInfo('----open accessory: '+strFullPath);
}
function returnBtn() {
if (isChange()) {
confirmSave();
}else {
if (bakRequestData) {
window.location.href = "index.html";
}else {
window.location.href = "black.html";
}
}
}
function confirmSave() {
var tSend={};
tSend['sequence_id']=Math.round(new Date() / 1000);
tSend['command']="request_confirm_save_project";
SendWXMessage( JSON.stringify(tSend) );
}
function RequestProjectInfo()
{
var tSend={};
tSend['sequence_id']=Math.round(new Date() / 1000);
tSend['command']="request_3mf_info";
SendWXMessage( JSON.stringify(tSend) );
}
function HandleStudio(pVal)
{
let strCmd=pVal['command'];
if(strCmd=='show_3mf_info')
{
updateInfo( pVal['model'] );
bakRequestData = JSON.parse(JSON.stringify(getCurrentRequestData()));
}
}
window.HandleEditor = handleEditorMessage;

View File

@@ -0,0 +1,155 @@
/*
* Simple Gallery (jQuery 2.1.1)
* 用法:
* $('#el').bsGallery({ images: ['a.jpg','b.jpg', ...] });
* 可选项:
* initialIndex, loop, thumbWidth, thumbHeight, mainHeight, counter
*/
(function($){
'use strict';
var defaults = {
images: [], // 必填:图片数组(字符串或 {src, thumb}
initialIndex: 0,
loop: true,
thumbHeight: 100,
mainHeight: 420,
// 新增:主图宽度与缩略栏宽度(可用数字或带单位字符串,如 '600px'、'60%'}
mainWidth: 560,
thumbsWidth: 180,
counter: true
};
function buildDom($root, settings){
$root.addClass('bs-gallery');
var $main = $('<div class="bs-gallery-main" />');
var $img = $('<img class="bs-gallery-image" alt="" />');
var $counter = $('<div class="bs-gallery-counter" />');
var $thumbs = $('<div class="bs-gallery-thumbs" />');
$main.append($img);
if (settings.counter) $main.append($counter);
$root.append($main).append($thumbs);
// 尺寸
$main.css({ height: settings.mainHeight + 'px' });
$thumbs.css({ maxHeight: settings.mainHeight + 'px' });
// 应用主图宽度与缩略栏宽度
if (settings.mainWidth != null) {
var mw = typeof settings.mainWidth === 'number' ? (settings.mainWidth + 'px') : settings.mainWidth;
$main.css({ width: mw });
// 固定主图宽度,避免被 flex 拉伸
$main.css({ flex: '0 0 ' + mw });
}
if (settings.thumbsWidth != null) {
var tw = typeof settings.thumbsWidth === 'number' ? (settings.thumbsWidth + 'px') : settings.thumbsWidth;
$thumbs.css({ width: tw });
}
// 生成缩略图
$.each(settings.images, function(i, it){
var src = typeof it === 'string' ? it : it.src;
var thumb = typeof it === 'string' ? it : (it.thumb || it.src);
var $t = $('<div class="bs-gallery-thumb" />').attr('data-index', i);
var $ti = $('<img />').attr('src', thumb).attr('alt','');
$t.append($ti);
$thumbs.append($t);
});
// 返回引用
return { $main:$main, $img:$img, $counter:$counter, $thumbs:$thumbs };
}
function plugin($root, options){
// 清理旧实例移除事件、数据与DOM支持重复初始化
$root.off('.bsGallery');
$root.removeData('bsGallery');
$root.removeClass('bs-gallery');
$root.empty();
var settings = $.extend({}, defaults, options||{});
if (!settings.images || !settings.images.length) return;
var ui = buildDom($root, settings);
var count = settings.images.length;
var index = Math.max(0, Math.min(settings.initialIndex, count - 1));
function srcAt(i){
var it = settings.images[i];
return typeof it === 'string' ? it : it.src;
}
function updateCounter(){
if (!settings.counter) return;
ui.$counter.text((index + 1) + '/' + count);
}
function setActiveThumb(){
ui.$thumbs.children('.bs-gallery-thumb').removeClass('active');
ui.$thumbs.children('.bs-gallery-thumb').eq(index).addClass('active');
// 确保可见
var $active = ui.$thumbs.children('.bs-gallery-thumb').eq(index);
var cTop = ui.$thumbs.scrollTop();
var cH = ui.$thumbs.height();
var tTop = $active.position().top + cTop;
var tH = $active.outerHeight();
if (tTop < cTop) ui.$thumbs.scrollTop(tTop);
else if (tTop + tH > cTop + cH) ui.$thumbs.scrollTop(tTop + tH - cH);
}
function show(i){
if (i === index) return;
index = i;
ui.$img.stop(true, true).fadeOut(120, function(){
ui.$img.attr('src', srcAt(index)).fadeIn(120);
});
updateCounter();
setActiveThumb();
}
function next(){
if (index < count - 1) show(index + 1);
else if (settings.loop) show(0);
}
function prev(){
if (index > 0) show(index - 1);
else if (settings.loop) show(count - 1);
}
// 事件:缩略图点击
ui.$thumbs.on('click', '.bs-gallery-thumb', function(){
var i = parseInt($(this).attr('data-index'), 10);
show(i);
});
// 主图点击下一张
ui.$main.on('click', function(){ next(); });
// 键盘左右切换(容器获取焦点时)
$root.attr('tabindex', 0);
$root.on('keydown.bsGallery', function(e){
if (e.which === 37) { // left
prev(); e.preventDefault();
} else if (e.which === 39) { // right
next(); e.preventDefault();
}
});
// 初始化显示
ui.$img.attr('src', srcAt(index));
setActiveThumb();
updateCounter();
// 暴露简单 API
$root.data('bsGallery', { next: next, prev: prev, show: show, index: function(){return index;}, count: function(){return count;} });
}
$.fn.bsGallery = function(options){
return this.each(function(){ plugin($(this), options); });
};
})(jQuery);

View File

@@ -0,0 +1,81 @@
// 侧边导航:导轨 + 活动短竖条
(function($){
function positionIndicator(){
var $nav = $('#sideNav');
var $active = $nav.find('.nav-item.active');
if (!$active.length) return;
var top = $active.position().top;
var height = $active.outerHeight();
$nav.find('.indicator').css({ top: top + 'px', height: height + 'px' });
}
function scrollToAnchor($item){
var href = $item.find('a').attr('href');
if (href && href.charAt(0) === '#'){
var $target = $(href);
if ($target.length){
var offset = 70; // 顶部预留,避免顶到视口边缘
$('html,body').animate({ scrollTop: Math.max(0, $target.offset().top - offset) }, 300);
}
}
}
$(function(){
positionIndicator();
$('#sideNav').on('click', '.nav-item', function(e){
e.preventDefault();
var $it = $(this);
$it.addClass('active').siblings('.nav-item').removeClass('active');
positionIndicator();
scrollToAnchor($it);
});
// 滚动联动ScrollSpy
var ticking = false;
function computeActiveByScroll(){
var scrollTop = $(window).scrollTop();
var viewportOffset = 100; // 判定阈值,提前一点触发
var $items = $('#sideNav .nav-item');
var currentId = null;
$items.each(function(){
var href = $(this).find('a').attr('href');
if (!href || href.charAt(0) !== '#') return;
var $sec = $(href);
if (!$sec.length) return;
var top = $sec.offset().top;
if (top <= scrollTop + viewportOffset){
currentId = href;
}
});
if (currentId){
var $cur = $items.filter(function(){
return $(this).find('a').attr('href') === currentId;
}).first();
if ($cur.length && !$cur.hasClass('active')){
$cur.addClass('active').siblings('.nav-item').removeClass('active');
positionIndicator();
} else {
// 位置可能变化,仍然校准指示条
positionIndicator();
}
}
}
function onScroll(){
if (!ticking){
ticking = true;
requestAnimationFrame(function(){
computeActiveByScroll();
ticking = false;
});
}
}
$(window).on('scroll', onScroll);
$(window).on('resize', function(){
positionIndicator();
computeActiveByScroll();
});
// 初始根据当前滚动位置激活一次
computeActiveByScroll();
});
})(jQuery);