/** * Zibll前端剧集视频扩展 - 前端脚本 * Version: 1.0.0 */ (function($, window) { 'use strict'; // 等待DOM加载完成 $(document).ready(function() { // 稍微延迟初始化,确保主题JS已执行 setTimeout(function() { ZFVEManager.init(); }, 500); }); /** * 剧集视频管理器 */ var ZFVEManager = { // 配置 config: { episodeCounter: 1, // 从第1集开始计数 maxEpisodes: 88, // 最大剧集数 }, // DOM元素缓存 elements: {}, // 媒体选择器实例 mediaSelector: null, // 当前操作的剧集输入框 currentEpisodeInput: null, /** * 初始化 */ init: function() { // 检查是否在投稿页面(通过标题输入框判断) if (!$('.newposts-title').length && !$('.featured-edit').length) { return; } // 插入UI到页面 this.insertUI(); this.cacheElements(); this.bindEvents(); this.checkVideoStatus(); this.loadExistingData(); }, /** * 插入UI到页面 */ insertUI: function() { var $template = $('#zfve-ui-template'); if (!$template.length) { return; } // 查找标题输入框 var $titleBox = $('.newposts-title'); if (!$titleBox.length) { return; } // 从模板中获取内容类型选择器(始终显示) var $contentType = $template.find('.zfve-content-type').clone(); // 从模板中获取剧集视频设置(仅视频教程显示) var $videoEpisodes = $template.find('.zfve-video-episodes-wrapper').clone(); // 从模板中获取资源介绍(始终显示) var $description = $template.find('.zfve-resource-description').clone(); // 从模板中获取文件格式管理(仅资源类型显示) var $fileFormats = $template.find('.zfve-file-formats').clone(); // 从模板中获取模型属性(仅资源类型显示) var $modelAttributes = $template.find('.zfve-model-attributes').clone(); // 从模板中获取引擎属性(仅引擎格式显示) var $engineAttributes = $template.find('.zfve-engine-attributes').clone(); // 从模板中获取资源图片(始终显示) var $resourceImages = $template.find('.zfve-resource-images').clone(); // 插入到标题输入框之后,按正确顺序排列 $titleBox.after($contentType); $contentType.after($videoEpisodes); $videoEpisodes.after($description); $description.after($fileFormats); $fileFormats.after($modelAttributes); $modelAttributes.after($engineAttributes); $engineAttributes.after($resourceImages); // 移除模板(避免重复ID) $template.remove(); }, /** * 缓存DOM元素 */ cacheElements: function() { this.elements = { wrapper: $('.zfve-video-episodes-wrapper'), addBtn: $('#zfve_add_episode'), episodesList: $('#zfve_episodes_list') }; }, /** * 绑定事件 */ bindEvents: function() { var self = this; // 添加剧集按钮 this.elements.addBtn.on('click', function(e) { e.preventDefault(); self.addEpisode(); }); // 删除剧集(使用事件委托) this.elements.episodesList.on('click', '.zfve-remove-episode', function(e) { e.preventDefault(); self.removeEpisode($(this)); }); // 上传视频按钮(使用事件委托) this.elements.episodesList.on('click', '.zfve-upload-video-btn', function(e) { e.preventDefault(); self.openVideoSelector($(this)); }); // 监听付费内容开关(子比主题的付费模块) this.initPayModuleListener(); // 实时同步资源介绍内容到原有编辑器 $('#post_content').on('input change', function() { self.syncContentToOriginalEditor(); }); // 拦截AJAX请求,确保图片数据被包含 if (typeof $.ajaxSetup === 'function') { var originalBeforeSend = $.ajaxSettings.beforeSend; $.ajaxSetup({ beforeSend: function(xhr, settings) { // 检查是否是表单提交的AJAX请求 if (settings.type === 'POST' && settings.data && typeof settings.data === 'string') { if (settings.data.indexOf('post_content') > -1 || settings.data.indexOf('posts_id') > -1) { // 合并图片到post_content if (typeof ZFVEImagePicker !== 'undefined' && ZFVEImagePicker.resourceImages.length > 0) { var descriptionText = $('#post_content').val() || ''; var imagesHtml = ''; ZFVEImagePicker.resourceImages.forEach(function(url) { imagesHtml += '\n\n资源图片'; }); var fullContent = descriptionText + imagesHtml; // 修改AJAX数据 var params = new URLSearchParams(settings.data); params.set('post_content', fullContent); settings.data = params.toString(); } } } if (originalBeforeSend) { return originalBeforeSend.apply(this, arguments); } } }); } // 表单提交前,更新隐藏的input值并验证 $('form.new-post-form').on('submit', function(e) { // 强制更新所有隐藏字段(确保最新数据被提交) if (typeof ZFVEFileFormats !== 'undefined' && ZFVEFileFormats.updateFormatsData) { ZFVEFileFormats.updateFormatsData(); } if (typeof ZFVEModelAttributes !== 'undefined' && ZFVEModelAttributes.saveAttributes) { ZFVEModelAttributes.saveAttributes(); } if (typeof ZFVEEngineAttributes !== 'undefined' && ZFVEEngineAttributes.saveAttributes) { ZFVEEngineAttributes.saveAttributes(); } // 验证资源文件格式 if (!self.validateFormats()) { e.preventDefault(); alert('错误:至少需要选择一个资源文件格式!'); return false; } // 将图片HTML直接合并到post_content中 if (typeof ZFVEImagePicker !== 'undefined' && ZFVEImagePicker.resourceImages.length > 0) { var descriptionText = $('#post_content').val() || ''; var imagesHtml = ''; ZFVEImagePicker.resourceImages.forEach(function(url) { imagesHtml += '\n\n资源图片'; }); var fullContent = descriptionText + imagesHtml; $('#post_content').val(fullContent); // 同时更新隐藏字段(兼容) ZFVEImagePicker.updateHiddenField(); } // 同步资源介绍到原有编辑器 self.syncContentToOriginalEditor(); self.updateHiddenInputs(); }); }, /** * 检查内容类型并显示/隐藏剧集设置 */ checkVideoStatus: function() { // 获取当前内容类型 var currentType = $('input[name="zfve_content_type"]:checked').val() || 'resource'; // 如果是视频教程类型,始终显示剧集设置(无论是否有视频封面) if (currentType === 'video') { this.elements.wrapper.slideDown(300); } else { // 如果是资源内容类型,隐藏剧集设置 this.elements.wrapper.slideUp(300); } }, /** * 初始化付费内容模块监听器 */ initPayModuleListener: function() { var self = this; // 子比主题常见的付费开关选择器 var payModuleSelectors = [ '#pay-module', // 付费模块开关 '[name="pay_module"]', // 付费模块name属性 '#posts_price', // 价格输入框 '[name="posts_price"]', // 价格name属性 '.pay-module-switch', // 付费模块switch类 '.posts-price-input', // 价格输入框类 'input[id*="pay"]', // ID包含pay的input 'input[name*="pay"]', // name包含pay的input 'input[id*="price"]', // ID包含price的input 'input[name*="price"]', // name包含price的input '.checkbox-wrapper input', // checkbox包装器中的input '.pay-setting input', // 付费设置中的input '.sidebar-tab-main input[type="checkbox"]' // 侧边栏checkbox ]; // 尝试找到付费开关 var $paySwitch = null; for (var i = 0; i < payModuleSelectors.length; i++) { $paySwitch = $(payModuleSelectors[i]); if ($paySwitch.length) { break; } } if (!$paySwitch || !$paySwitch.length) { // 延迟重试(付费模块可能延迟加载) setTimeout(function() { self.initPayModuleListener(); }, 3000); // 使用MutationObserver监听DOM变化 this.observePayModuleChanges(); return; } // 初始状态检查 this.updateEpisodePaymentStatus($paySwitch); // 监听开关变化(适配不同的输入类型) $paySwitch.on('change', function() { self.updateEpisodePaymentStatus($(this)); }); // 如果是价格输入框,也监听input事件 if ($paySwitch.is('input[type="text"], input[type="number"]')) { $paySwitch.on('input', function() { self.updateEpisodePaymentStatus($(this)); }); } // 如果是复选框,也监听click事件 if ($paySwitch.is('input[type="checkbox"]')) { $paySwitch.on('click', function() { setTimeout(function() { self.updateEpisodePaymentStatus($paySwitch); }, 100); }); } }, /** * 使用MutationObserver监听付费模块DOM变化 */ observePayModuleChanges: function() { var self = this; // 监听整个右侧边栏的变化 var sidebarSelectors = ['.sidebar-tab-main', '.post-edit-sidebar', '.edit-sidebar', '#side-sortables']; sidebarSelectors.forEach(function(selector) { var sidebar = document.querySelector(selector); if (sidebar) { var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.addedNodes.length) { setTimeout(function() { self.initPayModuleListener(); }, 500); } }); }); observer.observe(sidebar, { childList: true, subtree: true }); } }); }, /** * 更新剧集付费状态 */ updateEpisodePaymentStatus: function($paySwitch) { var isPayEnabled = false; // 判断付费功能是否开启 if ($paySwitch.is('input[type="checkbox"]')) { // 复选框类型 isPayEnabled = $paySwitch.is(':checked'); } else if ($paySwitch.is('input[type="text"], input[type="number"]')) { // 价格输入框,如果有值则认为开启付费 var price = $paySwitch.val(); isPayEnabled = price && price !== '' && parseFloat(price) > 0; } else if ($paySwitch.is('select')) { // 下拉选择框 var value = $paySwitch.val(); isPayEnabled = value && value !== '' && value !== '0'; } // 更新所有剧集的观看权限选择器状态 var $episodePaidSelects = $('.zfve-episode-is-paid'); if (isPayEnabled) { $episodePaidSelects.prop('disabled', false); $('.zfve-episode-is-paid').siblings('.px12').html( '已开启付费功能,可以设置付费剧集' ); } else { $episodePaidSelects.prop('disabled', true).val('0'); $('.zfve-episode-is-paid').siblings('.px12').html( '请先在右边栏开启"付费内容"功能后,才能设置付费剧集' ); } }, /** * 添加剧集 */ addEpisode: function() { if (this.elements.episodesList.find('.zfve-episode-item').length >= this.config.maxEpisodes) { this.showNotice('最多可添加' + this.config.maxEpisodes + '集', 'warning'); return; } var episodeNumber = this.config.episodeCounter++; var episodeHtml = this.createEpisodeHtml(episodeNumber); this.elements.episodesList.append(episodeHtml); // 添加淡入动画 var $newEpisode = this.elements.episodesList.find('.zfve-episode-item').last(); $newEpisode.addClass('zfve-fade-in'); // 滚动到新添加的剧集 this.scrollToEpisode($newEpisode); // 同步新剧集的付费状态(解决添加剧集后开启付费功能,新剧集状态不更新的bug) this.syncNewEpisodePaymentStatus(); }, /** * 同步新添加剧集的付费状态 */ syncNewEpisodePaymentStatus: function() { var self = this; // 尝试找到付费开关(使用相同的选择器列表) var payModuleSelectors = [ '#pay-module', '[name="pay_module"]', '#posts_price', '[name="posts_price"]', '.pay-module-switch', '.posts-price-input', 'input[id*="pay"]', 'input[name*="pay"]', 'input[id*="price"]', 'input[name*="price"]' ]; var $paySwitch = null; for (var i = 0; i < payModuleSelectors.length; i++) { $paySwitch = $(payModuleSelectors[i]); if ($paySwitch.length) { // 找到了付费开关,更新状态 setTimeout(function() { self.updateEpisodePaymentStatus($paySwitch); }, 100); return; } } // 如果找不到付费开关,检查现有剧集的状态 var $existingSelects = $('.zfve-episode-is-paid').not(':last'); if ($existingSelects.length > 0) { var isEnabled = !$existingSelects.first().prop('disabled'); var $newSelect = $('.zfve-episode-is-paid').last(); if (isEnabled) { // 如果现有剧集是启用的,新剧集也启用 $newSelect.prop('disabled', false); $newSelect.siblings('.px12').html( '已开启付费功能,可以设置付费剧集' ); } else { // 如果现有剧集是禁用的,新剧集也禁用 $newSelect.prop('disabled', true).val('0'); $newSelect.siblings('.px12').html( '请先在右边栏开启"付费内容"功能后,才能设置付费剧集' ); } } }, /** * 创建剧集HTML */ createEpisodeHtml: function(episodeNumber, title, url, isPaid) { title = title || ''; url = url || ''; isPaid = (isPaid === undefined || isPaid === null) ? false : isPaid; // 默认免费 return '
' + '
' + '第' + episodeNumber + '集' + '' + '' + '' + '
' + '
' + '
' + '' + '' + '
' + '
' + '' + '
' + '' + '' + '
' + '
支持本地视频以及m3u8、mpd、flv等流媒体格式
' + '
' + '
' + '' + '' + '
' + '请先在右边栏开启"付费内容"功能后,才能设置付费剧集' + '
' + '
' + '
' + '
'; }, /** * 删除剧集 */ removeEpisode: function($btn) { var self = this; var $episode = $btn.closest('.zfve-episode-item'); // 添加确认提示 if (confirm('确定要删除这个剧集吗?')) { $episode.fadeOut(300, function() { $(this).remove(); self.updateEpisodeNumbers(); }); } }, /** * 更新剧集编号 */ updateEpisodeNumbers: function() { var episodeNumber = 1; // 从第1集开始 this.elements.episodesList.find('.zfve-episode-item').each(function() { var $item = $(this); $item.attr('data-episode', episodeNumber); $item.find('.zfve-episode-number').text('第' + episodeNumber + '集'); // 更新placeholder var $titleInput = $item.find('.zfve-episode-title'); if (!$titleInput.val()) { $titleInput.attr('placeholder', '第' + episodeNumber + '集'); } episodeNumber++; }); this.config.episodeCounter = episodeNumber; }, /** * 打开视频选择器 */ openVideoSelector: function($btn) { var self = this; this.currentEpisodeInput = $btn.closest('.zfve-video-input-group').find('.zfve-episode-url'); // 检查是否有zib.media(Zibll主题的媒体选择器) if (typeof zib === 'undefined' || typeof zib.media === 'undefined') { this.showNotice('媒体选择器未加载,请确保Zibll主题已正确安装', 'danger'); return; } // 如果还没有创建媒体选择器,则创建 if (!this.mediaSelector) { var args = { type: 'video', is_upload: true, iframe: false, upload_multiple: 1, upload_size: window._win.upload_video_size || 30, multiple: 1, text: { title: '选择视频', subtitle: '请选择或上传视频文件' } }; this.mediaSelector = new zib.media(args); // 绑定选择事件 this.mediaSelector.$el.on('lists_submit upload', function(e, data) { var video = data.data ? data.data[0] : data; if (video && video.url && self.currentEpisodeInput) { self.currentEpisodeInput.val(video.url); } self.mediaSelector.close(); }); // 绑定手动输入事件 this.mediaSelector.$el.on('input_submit', function(e, data) { if (data.vals && data.vals[0] && self.currentEpisodeInput) { self.currentEpisodeInput.val(data.vals[0]); } }); } // 打开媒体选择器 this.mediaSelector.resetActiveLists(); this.mediaSelector.open(); }, /** * 验证文件格式 */ validateFormats: function() { // 检查是否为资源内容类型 var contentType = $('input[name="zfve_content_type"]:checked').val(); if (contentType !== 'resource') { // 如果是视频类型,不需要验证文件格式 return true; } // 检查文件格式数量 var totalFormats = $('.zfve-format-item').length; if (totalFormats < 1) { return false; } // 检查是否至少有一个格式选择了类型 var hasValidFormat = false; $('.zfve-format-item').each(function() { var formatType = $(this).find('.format-type-select').val(); if (formatType && formatType !== '') { hasValidFormat = true; return false; // 找到一个有效格式就退出循环 } }); return hasValidFormat; }, /** * 同步内容到原有编辑器 */ syncContentToOriginalEditor: function() { var content = $('#post_content').val() || ''; // 同步到TinyMCE编辑器 if (typeof tinymce !== 'undefined') { var editor = tinymce.get('post_content'); if (editor) { editor.setContent(content); } } // 同步到所有name="post_content"的元素 $('textarea[name="post_content"], input[name="post_content"]').each(function() { var $elem = $(this); if ($elem.attr('id') !== 'post_content') { $elem.val(content); } }); // 同步到WP编辑器的textarea var $wpEditor = $('#wp-post_content-editor-container textarea'); if ($wpEditor.length) { $wpEditor.val(content); } // 触发change事件确保表单验证能检测到 $('#post_content').trigger('change').trigger('input'); }, /** * 更新隐藏的input值(用于表单提交) */ updateHiddenInputs: function() { // 移除旧的隐藏input $('.zfve-hidden-input').remove(); var episodes = []; this.elements.episodesList.find('.zfve-episode-item').each(function() { var $item = $(this); var title = $item.find('.zfve-episode-title').val(); var url = $item.find('.zfve-episode-url').val(); var isPaid = $item.find('.zfve-episode-is-paid').val() === '1'; if (url) { // 只保存有URL的剧集 episodes.push({ title: title, url: url, is_paid: isPaid }); } }); // 添加隐藏input var $form = $('form.new-post-form'); episodes.forEach(function(episode, index) { $form.append(''); $form.append(''); $form.append(''); }); }, /** * 加载已有数据(编辑模式) */ loadExistingData: function() { var self = this; // 检查是否有编辑数据 if (typeof window.zfve_edit_data === 'undefined') { return; } var editData = window.zfve_edit_data; // 延迟加载,确保其他模块都已初始化 setTimeout(function() { // 加载资源介绍 if (editData.description) { $('#post_content').val(editData.description); } // 加载内容类型 if (editData.content_type) { $('input[name="zfve_content_type"][value="' + editData.content_type + '"]').prop('checked', true); $('#zfve_content_type_value').val(editData.content_type); // 触发内容类型切换 if (typeof ZFVEContentType !== 'undefined' && ZFVEContentType.switchContentType) { ZFVEContentType.switchContentType(editData.content_type); } } // 加载文件格式 if (editData.file_formats && typeof ZFVEFileFormats !== 'undefined' && ZFVEFileFormats.loadFormats) { ZFVEFileFormats.loadFormats(editData.file_formats); } // 加载模型属性 if (editData.model_attributes && typeof ZFVEModelAttributes !== 'undefined' && ZFVEModelAttributes.loadAttributes) { ZFVEModelAttributes.loadAttributes(editData.model_attributes); } // 加载引擎属性 if (editData.engine_attributes && typeof ZFVEEngineAttributes !== 'undefined' && ZFVEEngineAttributes.loadAttributes) { ZFVEEngineAttributes.loadAttributes(editData.engine_attributes); } // 加载剧集列表 if (editData.episodes && Array.isArray(editData.episodes) && editData.episodes.length > 0) { editData.episodes.forEach(function(episode, index) { var episodeNumber = index + 1; // 从第1集开始 var isPaid = (episode.is_paid === undefined || episode.is_paid === null) ? false : episode.is_paid; var episodeHtml = self.createEpisodeHtml( episodeNumber, episode.title, episode.url, isPaid ); self.elements.episodesList.append(episodeHtml); self.config.episodeCounter = episodeNumber + 1; }); } }, 200); // 延迟200ms确保其他模块已初始化 }, /** * 滚动到指定剧集 */ scrollToEpisode: function($episode) { if (!$episode.length) return; var container = this.elements.episodesList; var scrollTo = $episode.position().top + container.scrollTop() - 20; container.animate({ scrollTop: scrollTo }, 300); }, /** * 显示通知 */ showNotice: function(message, type) { type = type || 'info'; // 使用Zibll主题的通知系统 if (typeof zib !== 'undefined' && typeof zib.notify !== 'undefined') { zib.notify(message, type); } else { // 降级方案:使用alert alert(message); } }, /** * 转义HTML */ escapeHtml: function(text) { if (!text) return ''; var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; }); } }; // 导出到全局(如果需要外部访问) window.ZFVEManager = ZFVEManager; /** * 图片选择器管理器 */ var ZFVEImagePicker = { selectedImages: [], currentPage: 1, totalPages: 1, resourceImages: [], // 存储资源图片的URL数组 /** * 初始化 */ init: function() { this.bindEvents(); this.loadExistingImages(); }, /** * 绑定事件 */ bindEvents: function() { var self = this; // 添加图片按钮 $(document).on('click', '#zfve_add_images_btn', function(e) { e.preventDefault(); self.openPicker(); }); // 关闭选择器 $(document).on('click', '#zfve_picker_cancel_btn, .zfve-picker-backdrop, .zfve-picker-close', function() { self.closePicker(); }); // 确认选择 $(document).on('click', '#zfve_picker_confirm_btn', function() { self.confirmSelection(); }); // 上传按钮 $(document).on('click', '#zfve_picker_upload_btn', function() { $('#zfve_picker_file_input').click(); }); // 文件选择 $(document).on('change', '#zfve_picker_file_input', function(e) { self.uploadImages(e.target.files); }); // 搜索 var searchTimeout; $(document).on('input', '#zfve_picker_search', function() { clearTimeout(searchTimeout); var search = $(this).val(); searchTimeout = setTimeout(function() { self.currentPage = 1; self.loadImages(search); }, 500); }); // 删除图片 $(document).on('click', '.zfve-remove-image', function(e) { e.preventDefault(); e.stopPropagation(); var $item = $(this).closest('.zfve-image-item'); var url = $item.data('url'); self.removeImage(url); }); // 表单提交前更新隐藏字段 $('form').on('submit', function() { self.updateHiddenField(); }); }, /** * 加载已有图片 */ loadExistingImages: function() { if (window.zfve_edit_data && window.zfve_edit_data.images) { this.resourceImages = window.zfve_edit_data.images; this.renderGallery(); } }, /** * 打开选择器 */ openPicker: function() { this.selectedImages = []; this.currentPage = 1; $('#zfve_image_picker').fadeIn(300); $('body').css('overflow', 'hidden'); this.loadImages(); }, /** * 关闭选择器 */ closePicker: function() { $('#zfve_image_picker').fadeOut(300); $('body').css('overflow', ''); }, /** * 加载图片列表 */ loadImages: function(search) { var self = this; var container = $('#zfve_picker_grid'); container.html('
加载中...
'); $.ajax({ url: zfveData.ajaxUrl, type: 'POST', data: { action: 'zfve_get_user_images', page: self.currentPage, search: search || '', per_page: 30 }, success: function(response) { if (response.success && response.data.images) { var images = response.data.images; self.totalPages = response.data.total_pages || 1; if (images.length === 0) { container.html('

暂无图片,请先上传图片

'); return; } var html = ''; images.forEach(function(img) { var selected = self.selectedImages.indexOf(img.url) !== -1 ? 'selected' : ''; html += '
'; html += '
'; html += ''; html += '
'; html += '
'; html += '
'; }); container.html(html); self.updatePagination(); // 绑定点击事件 $('.zfve-picker-item').on('click', function() { var url = $(this).data('url'); var index = self.selectedImages.indexOf(url); if (index === -1) { self.selectedImages.push(url); $(this).addClass('selected'); } else { self.selectedImages.splice(index, 1); $(this).removeClass('selected'); } self.updateSelectedCount(); }); } else { container.html('

加载失败,请重试

'); } }, error: function() { container.html('

加载失败,请检查网络

'); } }); }, /** * 更新分页 */ updatePagination: function() { var self = this; var html = ''; if (self.currentPage > 1) { html += ''; } html += '第 ' + self.currentPage + ' / ' + self.totalPages + ' 页'; if (self.currentPage < self.totalPages) { html += ''; } $('#zfve_picker_pagination').html(html); $('.zfve-picker-page-btn').on('click', function() { self.currentPage = parseInt($(this).data('page')); self.loadImages($('#zfve_picker_search').val()); }); }, /** * 更新选中数量 */ updateSelectedCount: function() { var count = this.selectedImages.length; var text = count > 0 ? '添加选中的图片 (' + count + ')' : '请选择图片'; $('#zfve_picker_confirm_btn').text(text); $('#zfve_picker_confirm_btn').prop('disabled', count === 0); }, /** * 确认选择 */ confirmSelection: function() { var self = this; self.selectedImages.forEach(function(url) { if (self.resourceImages.indexOf(url) === -1) { self.resourceImages.push(url); } }); self.renderGallery(); self.updateHiddenField(); self.closePicker(); }, /** * 上传图片 */ uploadImages: function(files) { var self = this; if (files.length === 0) return; var formData = new FormData(); for (var i = 0; i < files.length; i++) { formData.append('files[]', files[i]); } formData.append('action', 'zfve_upload_images'); formData.append('nonce', zfveData.uploadNonce); $('#zfve_picker_upload_progress').show(); $('#zfve_picker_progress_bar').css('width', '0%'); $.ajax({ url: zfveData.ajaxUrl, type: 'POST', data: formData, processData: false, contentType: false, xhr: function() { var xhr = new window.XMLHttpRequest(); xhr.upload.addEventListener('progress', function(evt) { if (evt.lengthComputable) { var percentComplete = (evt.loaded / evt.total) * 100; $('#zfve_picker_progress_bar').css('width', percentComplete + '%'); } }, false); return xhr; }, success: function(response) { $('#zfve_picker_upload_progress').fadeOut(); $('#zfve_picker_file_input').val(''); if (response.success) { self.currentPage = 1; self.loadImages(); } else { alert('上传失败:' + (response.data || '未知错误')); } }, error: function() { $('#zfve_picker_upload_progress').fadeOut(); alert('上传失败,请重试'); } }); }, /** * 删除图片 */ removeImage: function(url) { var index = this.resourceImages.indexOf(url); if (index !== -1) { this.resourceImages.splice(index, 1); this.renderGallery(); this.updateHiddenField(); } }, /** * 渲染图片画廊 */ renderGallery: function() { var gallery = $('#zfve_images_gallery'); if (gallery.length === 0) { return; } if (this.resourceImages.length === 0) { gallery.html( '
' + '' + '

暂无图片,点击上方"添加图片"按钮开始上传

' + '
' ); } else { var html = ''; this.resourceImages.forEach(function(url) { html += '
'; html += '资源图片'; html += ''; html += '
'; }); gallery.html(html); } }, /** * 更新隐藏字段 - 将图片转为HTML格式 */ updateHiddenField: function() { var html = ''; this.resourceImages.forEach(function(url) { html += '

资源图片

'; }); $('#zfve_resource_images_html').val(html); } }; // 初始化图片选择器 $(document).ready(function() { setTimeout(function() { ZFVEImagePicker.init(); }, 600); }); // 导出到全局 window.ZFVEImagePicker = ZFVEImagePicker; })(jQuery, window); /** * 瀑布流布局管理器 - 真正的Masonry布局 * 用于前端文章页面的图片展示 */ (function($, window) { 'use strict'; var ZFVEWaterfallLayout = { // 配置 config: { columnGap: 15, // 列间距 defaultColumns: 4, // 默认列数 animationDelay: 50, // 动画延迟(毫秒) heightVariationMin: 0.5, // 最小高度变化系数(50%) heightVariationMax: 2.2, // 最大高度变化系数(220%) useRandomHeights: true // 是否使用随机高度变化 }, // 当前列数 currentColumns: 4, // 等待DOM加载完成后初始化 init: function() { var self = this; // 查找所有瀑布流容器 var $galleries = $('.zfve-waterfall-gallery'); if ($galleries.length === 0) { return; } // 为每个容器初始化布局 $galleries.each(function() { self.initGallery($(this)); }); // 监听窗口大小变化 var resizeTimer; $(window).on('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(function() { $galleries.each(function() { self.layoutGallery($(this)); }); }, 250); }); }, /** * 初始化单个画廊 */ initGallery: function($gallery) { var self = this; var $items = $gallery.find('.zfve-waterfall-item'); if ($items.length === 0) { return; } // 等待所有图片加载完成 var images = $items.find('img'); var loadedCount = 0; var totalImages = images.length; if (totalImages === 0) { // 没有图片,直接布局 self.layoutGallery($gallery); return; } images.each(function() { var img = this; // 图片已加载或加载失败都继续 var checkLoad = function() { loadedCount++; if (loadedCount === totalImages) { self.layoutGallery($gallery); } }; if (img.complete) { checkLoad(); } else { $(img).on('load error', checkLoad); } }); }, /** * 计算当前应该使用的列数 */ getColumnCount: function() { var width = $(window).width(); if (width <= 480) { return 2; // 手机:2列 } else if (width <= 768) { return 2; // 平板:2列 } else if (width <= 1200) { return 3; // 中等屏幕:3列 } else { return 4; // 大屏幕:4列 } }, /** * 执行瀑布流布局 */ layoutGallery: function($gallery) { var self = this; var $items = $gallery.find('.zfve-waterfall-item'); if ($items.length === 0) { return; } // 计算列数 var columns = self.getColumnCount(); self.currentColumns = columns; // 计算容器宽度和列宽 var containerWidth = $gallery.width(); var gap = self.config.columnGap; var columnWidth = (containerWidth - (gap * (columns - 1))) / columns; // 初始化每列的高度数组 var columnHeights = []; for (var i = 0; i < columns; i++) { columnHeights[i] = 0; } // 为每个项目计算位置 $items.each(function(index) { var $item = $(this); var $img = $item.find('img'); // 获取图片的原始尺寸 var img = $img[0]; var imgWidth = img.naturalWidth || img.width || 800; var imgHeight = img.naturalHeight || img.height || 600; // 如果无法获取图片尺寸,使用随机高度范围 if (!img.naturalWidth && !img.width) { var randomHeights = [200, 300, 400, 500]; imgHeight = randomHeights[index % randomHeights.length]; imgWidth = columnWidth; } // 计算项目高度(保持宽高比) var aspectRatio = imgHeight / imgWidth; var itemHeight = aspectRatio * columnWidth; // 添加高度变化范围,让布局更自然 if (self.config.useRandomHeights) { // 使用预设的随机系数数组,循环使用 var heightVariations = [1, 2, 3]; var heightVariation = heightVariations[index % heightVariations.length]; itemHeight = itemHeight * heightVariation; } else { var heightVariation = 1.0; } // 限制高度范围,避免过高或过低 var minItemHeight = columnWidth * 0.5; // 最小高度为列宽的50% var maxItemHeight = columnWidth * 4.0; // 最大高度为列宽的400%(支持3倍系数) itemHeight = Math.max(minItemHeight, Math.min(maxItemHeight, itemHeight)); // 找到当前最短的列 var minHeight = Math.min.apply(Math, columnHeights); var columnIndex = columnHeights.indexOf(minHeight); // 计算位置 var left = columnIndex * (columnWidth + gap); var top = columnHeights[columnIndex]; // 设置项目的位置和尺寸 $item.css({ 'position': 'absolute', 'left': left + 'px', 'top': top + 'px', 'width': columnWidth + 'px', 'height': itemHeight + 'px' }); // 添加定位完成的类,触发显示动画 setTimeout(function() { $item.addClass('zfve-positioned'); }, self.config.animationDelay * index); // 更新该列的高度 columnHeights[columnIndex] += itemHeight + gap; }); // 设置容器高度为最高列的高度 var maxHeight = Math.max.apply(Math, columnHeights); $gallery.css('height', maxHeight + 'px'); } }; // DOM加载完成后初始化 $(document).ready(function() { // 延迟初始化,确保DOM完全渲染 setTimeout(function() { ZFVEWaterfallLayout.init(); }, 300); }); // 导出到全局 window.ZFVEWaterfallLayout = ZFVEWaterfallLayout; })(jQuery, window); 模拟-ITCG资源网
模拟-ITCG资源网
ITCG资源网
  • 首页
  • 资源商城
    • 3D模型资源
    • Unreal Engine资源
    • Unity资源
  • 作品集
  • 教程
    • 入门教程
      • 入门3Dsmax教程
      • 入门Maya教程
      • 入门Blender教程
      • 入门Unity教程
      • 入门Unreal Engine教程
    • 专项教程
      • 3Dsmax
      • Maya
      • Blender
      • Unreal Engine
      • Unity
  • 免费
    • 本月免费
    • 永久免费
  • 盒子AR
  • 工具软件
  • 技术问答
  • 更多
    • 声明
  • 登录
  • 注册
  • 首页
  • 资源商城
    • 3D模型资源
    • Unreal Engine资源
    • Unity资源
  • 作品集
  • 教程
    • 入门教程
    • 专项教程
  • 免费
    • 本月免费
    • 永久免费
  • 盒子AR
  • 工具软件
  • 技术问答
  • 更多
    • 声明
登录
注册
找回密码

快速登录

模拟

排序
    更新浏览点赞评论售价销量
补光灯的资产制作和交互功能开发(1)-ITCG资源网

补光灯的资产制作和交互功能开发(1)

Unreal Engine专项教程
586
电风扇模拟仿真-ITCG资源网

电风扇模拟仿真

免费资源Unreal Engine资源场景实验免费本月免费
260
UE5物体变形模拟解算-ITCG资源网

UE5物体变形模拟解算

免费资源Unreal Engine专项教程
54
  • ITCG资源网

    一站式3D数字资产分享与技术问答平台

    这里是面向3D互动仿真开发者的专业开源技术社区论坛,汇集了大量专业的3D开发原创内容,以分享专业.优质.高效的技术和经验为己任,为所有学习者提供平台
  • AI ITCG ITCG Hub

    Copyright © 2022 · ITCG资源网 · 由贵州虚幻数字科技.强力驱动 黔ICP备2022006773号-3 贵公网安备 52030002001287号
    扫一扫加微信-ITCG资源网
  • 公众号-ITCG资源网

    公众号

    平台管理-ITCG资源网

    平台管理
扫码添加微信-ITCG资源网
在手机上浏览此页面

ITCG资源网
登录
没有账号?立即注册
邮箱
验证码
账号密码登录
用户名或邮箱
登录密码
找回密码 | 免密登录

社交账号登录

注册
已有账号,立即登录
设置用户名
邮箱
验证码
设置密码
重复密码
扫码登录
使用其它方式登录或注册扫码登录

作品发布指南

通过平台发布3D作品,系统将自动生成专属在线展示应用。无论是求职面试时展示专业能力,还是向客户呈现项目效果,都能让您的创作以更直观的方式呈现。只需分享作品首页链接,即可让合作伙伴与朋友轻松了解您的创意成果。

  • 专业数据加密保护
    您发布的3D作品将全程采用行业领先的加密技术处理,让您在分享创意成果时无需担忧知识产权安全,真正实现安心便捷的作品传播。
  • 作品发布后暂未显示说明
    作品提交发布后,系统将自动进入后台审核流程,这是为了确保平台内容质量与合规性。审核通过后,您的作品会立即在"作品栏"中展示。

如有任何疑问,请联系我们的客服。

发布作品