1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260 |
- define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
- Fast.config.openArea = ['80%', '80%'];
- var vm, si;
- // 选项卡验证器模块
- var TabValidator = {
- steps: [
- {
- id: '#basics',
- name: '基础信息',
- required: ['row[type]', 'row[goods_sn]', 'row[title]', 'row[category_ids]', 'row[image]', 'row[images]'],
- conditionalRequired: {
- 'row[supplier_id]': function() { return true; },
- 'row[inspection_type_id]': function() { return true; }
- }
- },
- {
- id: '#skus',
- name: '价格库存',
- required: [],
- customValidation: 'validatePriceStock'
- },
- {
- id: '#delivery',
- name: '配送设置',
- required: ['row[delivery_type]'],
- conditionalRequired: {
- 'row[express_freight]': function() {
- return $('input[name="row[delivery_type]"]:checked').val() === 'EXPRESS' &&
- $('input[name="row[express_type]"]:checked').val() === '2';
- },
- 'row[express_template_id]': function() {
- return $('input[name="row[delivery_type]"]:checked').val() === 'EXPRESS' &&
- $('input[name="row[express_type]"]:checked').val() === '3';
- }
- }
- },
- {
- id: '#detail',
- name: '商品详情',
- required: [],
- optional: true
- },
- {
- id: '#params',
- name: '商品参数',
- required: [],
- optional: true
- },
- {
- id: '#sales',
- name: '销售设置',
- required: ['row[stock_show_type]', 'row[sales_show_type]']
- }
- ],
- currentStep: 0,
- completedSteps: [],
- stepValidationResults: {},
- init: function() {
- this.addValidationStyles();
- this.bindNiceValidatorEvents();
- this.bindTabEvents();
- this.initStepNavigation();
- },
-
- bindNiceValidatorEvents: function() {
- var self = this;
-
- // 监听 Nice-validator 的验证事件
- $(document).on('valid.form', 'form[role="form"]', function(e, obj) {
- // 表单验证通过时清除所有选项卡错误状态
- self.clearTabErrors();
- });
-
- $(document).on('invalid.form', 'form[role="form"]', function(e, obj) {
- // 表单验证失败时处理错误
- if (obj && obj.errors && obj.errors.length > 0) {
- self.handleNiceValidatorErrors(obj.errors);
- } else if (obj && obj.element) {
- // 单个字段错误
- var fieldName = obj.element.name || obj.element.id;
- var errorMessage = obj.msg || obj.message || '字段验证失败';
- self.handleFieldError(fieldName, errorMessage);
- }
- });
-
- $(document).on('invalid.field', 'form[role="form"]', function(e, obj) {
- // 单个字段验证失败时处理
- if (obj && obj.element) {
- var fieldName = obj.element.name || obj.element.id;
- var errorMessage = obj.msg || obj.message || '字段验证失败';
- self.handleFieldError(fieldName, errorMessage);
- }
- });
- // 监听FastAdmin的验证器事件 - 兼容性处理
- $(document).on('fa.event.validator', function(e, obj) {
- if (obj && obj.errors && obj.errors.length > 0) {
- self.handleNiceValidatorErrors(obj.errors);
- }
- });
- },
- addValidationStyles: function() {
- if ($('#goods-validation-styles').length > 0) return;
-
- var styles = `
- <style id="goods-validation-styles">
- .nav-tabs > li > a.tab-error { color: #d9534f !important; }
- .nav-tabs > li > a .error-icon { color: #d9534f; margin-left: 5px; }
- </style>
- `;
- $('head').append(styles);
- },
- bindTabEvents: function() {
- var self = this;
- $('.nav-tabs a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
- self.clearTabErrors();
- var target = $(e.target).attr('href');
- for (var i = 0; i < self.steps.length; i++) {
- if (self.steps[i].id === target) {
- self.currentStep = i;
- self.updateButtons();
- break;
- }
- }
- });
- $(document).on('invalid.field invalid.form', 'form[data-toggle="validator"]', function(e, data) {
- if (data && data.element) {
- var fieldName = data.element.name || data.element.id;
- var errorMessage = data.message || '字段验证失败';
- self.handleValidationError(errorMessage, fieldName);
- }
- });
- },
-
- handleNiceValidatorErrors: function(errors) {
- var self = this;
- var errorsByTab = {};
-
- // 按选项卡分组错误
- for (var i = 0; i < errors.length; i++) {
- var error = errors[i];
- var fieldName = error.element ? error.element.name : '';
- var tabId = self.getTabByFieldName(fieldName);
-
- if (tabId && !errorsByTab[tabId]) {
- errorsByTab[tabId] = [];
- }
-
- if (tabId) {
- errorsByTab[tabId].push({
- field: fieldName,
- message: error.msg || '验证失败',
- element: error.element
- });
- }
- }
-
- // 为每个有错误的选项卡显示错误状态
- for (var tabId in errorsByTab) {
- var tabErrors = errorsByTab[tabId];
- var errorMessages = tabErrors.map(function(err) { return err.message; });
- this.showTabError(tabId, errorMessages.join('、'));
- }
-
- // 跳转到第一个有错误的选项卡
- var firstErrorTab = Object.keys(errorsByTab)[0];
- if (firstErrorTab) {
- var stepIndex = this.getStepIndexByTabId(firstErrorTab);
- if (stepIndex !== -1) {
- this.goToStep(stepIndex);
- }
- }
- },
-
- handleFieldError: function(fieldName, errorMessage) {
- var tabId = this.getTabByFieldName(fieldName);
- if (tabId) {
- this.showTabError(tabId, errorMessage);
- var stepIndex = this.getStepIndexByTabId(tabId);
- if (stepIndex !== -1) {
- this.goToStep(stepIndex);
- }
- }
- },
-
- getStepIndexByTabId: function(tabId) {
- for (var i = 0; i < this.steps.length; i++) {
- if (this.steps[i].id === tabId) {
- return i;
- }
- }
- return -1;
- },
- initStepNavigation: function() {
- var self = this;
- this.updateButtons();
- $('.btn-prev').off('click.step').on('click.step', function() {
- if (self.currentStep > 0) {
- self.goToStep(self.currentStep - 1);
- }
- });
- $('.btn-next').off('click.step').on('click.step', function() {
- // 直接跳转到下一步,无需验证
- self.clearTabError(self.steps[self.currentStep].id);
- if (self.completedSteps.indexOf(self.currentStep) === -1) {
- self.completedSteps.push(self.currentStep);
- }
- if (self.currentStep < self.steps.length - 1) {
- self.goToStep(self.currentStep + 1);
- }
- });
- $('.nav-tabs a[data-toggle="tab"]').off('click.step').on('click.step', function(e) {
- e.preventDefault();
- var targetTabId = $(this).attr('href');
- var targetStepIndex = self.steps.findIndex(function(step) {
- return step.id === targetTabId;
- });
-
- if (targetStepIndex !== -1) {
- // 允许自由切换选项卡,无需验证前置步骤
- self.goToStep(targetStepIndex);
- }
- });
- },
- goToStep: function(stepIndex) {
- if (stepIndex < 0 || stepIndex >= this.steps.length) return;
-
- $('.tab-pane').removeClass('active in');
- $(this.steps[stepIndex].id).addClass('active in');
- $('.nav-tabs li').removeClass('active');
- $('.nav-tabs a[href="' + this.steps[stepIndex].id + '"]').parent().addClass('active');
-
- this.currentStep = stepIndex;
- this.updateButtons();
- $('html, body').animate({scrollTop: 0}, 300);
- },
- updateButtons: function() {
- var $prevBtn = $('.btn-prev');
- var $nextBtn = $('.btn-next');
- var $submitBtn = $('.btn-submit');
-
- if (this.currentStep === 0) {
- $prevBtn.hide();
- } else {
- $prevBtn.show();
- }
-
- if (this.currentStep === this.steps.length - 1) {
- $nextBtn.hide();
- $submitBtn.show();
- } else {
- $nextBtn.show();
- $submitBtn.hide();
- }
-
- $('.step-current').text(this.currentStep + 1);
- $('.step-total').text(this.steps.length);
- $('.step-text').text(this.steps[this.currentStep].name);
-
- var progress = ((this.currentStep + 1) / this.steps.length) * 100;
- $('.step-progress-bar').css('width', progress + '%');
- },
- validateCurrentStep: function() {
- var step = this.steps[this.currentStep];
- var isValid = true;
- var errors = [];
- var errorFields = [];
-
- step.required.forEach(function(fieldName) {
- var fieldResult = Controller.validateField(fieldName);
- if (!fieldResult.isValid) {
- isValid = false;
- errors.push(fieldResult.label);
- errorFields.push(fieldResult.element);
- }
- });
-
- if (step.conditionalRequired) {
- for (var fieldName in step.conditionalRequired) {
- var condition = step.conditionalRequired[fieldName];
- if (condition && typeof condition === 'function' && condition()) {
- var fieldResult = Controller.validateField(fieldName);
- if (!fieldResult.isValid) {
- isValid = false;
- errors.push(fieldResult.label);
- errorFields.push(fieldResult.element);
- }
- }
- }
- }
-
- if (step.customValidation && Controller[step.customValidation]) {
- var customResult = Controller[step.customValidation]();
- if (!customResult.isValid) {
- isValid = false;
- errors = errors.concat(customResult.errors);
- if (customResult.errorFields) {
- errorFields = errorFields.concat(customResult.errorFields);
- }
- }
- }
-
- this.stepValidationResults[this.currentStep] = {
- isValid: isValid,
- errors: errors,
- errorFields: errorFields
- };
-
- if (!isValid) {
- var message = errors.length > 0 ?
- '请完善以下必填项:\n' + errors.join('、') :
- '当前步骤存在验证错误,请检查后重试';
- Toastr.error(message);
- this.highlightErrorFields(step.id, errorFields);
- this.showTabError(step.id, message);
- }
-
- return isValid;
- },
- validateAllSteps: function() {
- var result = {
- isValid: true,
- firstErrorStep: -1,
- errorSteps: [],
- allErrors: {}
- };
-
- for (var i = 0; i < this.steps.length; i++) {
- var step = this.steps[i];
- var stepValid = true;
- var stepErrors = [];
-
- step.required.forEach(function(fieldName) {
- var fieldResult = Controller.validateField(fieldName);
- if (!fieldResult.isValid) {
- stepValid = false;
- stepErrors.push(fieldResult.label);
- }
- });
-
- if (step.conditionalRequired) {
- for (var fieldName in step.conditionalRequired) {
- var condition = step.conditionalRequired[fieldName];
- if (condition && typeof condition === 'function' && condition()) {
- var fieldResult = Controller.validateField(fieldName);
- if (!fieldResult.isValid) {
- stepValid = false;
- stepErrors.push(fieldResult.label);
- }
- }
- }
- }
-
- if (step.customValidation && Controller[step.customValidation]) {
- var customResult = Controller[step.customValidation]();
- if (!customResult.isValid) {
- stepValid = false;
- stepErrors = stepErrors.concat(customResult.errors);
- }
- }
-
- if (!stepValid) {
- result.isValid = false;
- if (result.firstErrorStep === -1) {
- result.firstErrorStep = i;
- }
- result.errorSteps.push(i);
- result.allErrors[i] = {
- stepName: step.name,
- errors: stepErrors
- };
- this.showTabError(step.id, stepErrors.join('、'));
- }
- }
-
- return result;
- },
- getTabByFieldName: function(fieldName) {
- var fieldTabMap = {
- // 基础信息tab
- 'type': '#basics', 'goods_sn': '#basics', 'title': '#basics', 'sub_title': '#basics', 'subtitle': '#basics',
- 'category_id': '#basics', 'category_ids': '#basics', 'brand_id': '#basics',
- 'image': '#basics', 'images': '#basics', 'label_ids': '#basics', 'guarantee_ids': '#basics',
- 'unit_id': '#basics', 'supplier_id': '#basics', 'inspection_type_id': '#basics', 'weigh': '#basics',
- 'online_type': '#basics', 'scheduled_online_time': '#basics',
- 'is_auto_offline': '#basics', 'scheduled_offline_time': '#basics',
-
- // 价格库存tab
- 'spec_type': '#skus', 'price': '#skus', 'lineation_price': '#skus',
- 'cost_price': '#skus', 'stocks': '#skus', 'weight': '#skus', 'volume': '#skus',
- 'sku_sn': '#skus', 'skus': '#skus', 'spec': '#skus',
-
- // 配送设置tab
- 'delivery_type': '#delivery', 'express_type': '#delivery', 'express_freight': '#delivery',
- 'express_template_id': '#delivery',
-
- // 商品详情tab
- 'content': '#detail',
-
- // 商品参数tab
- 'params': '#params', 'attribute_ids': '#params',
-
- // 销售设置tab
- 'stock_show_type': '#sales', 'sales_show_type': '#sales', 'virtual_sales': '#sales',
- 'keywords': '#sales', 'description': '#sales', 'is_hot': '#sales'
- };
-
- // 清理字段名,支持多种格式
- var cleanFieldName = fieldName;
- if (fieldName.indexOf('row[') === 0) {
- cleanFieldName = fieldName.replace(/^row\[/, '').replace(/\]$/, '');
- } else if (fieldName.indexOf('c-') === 0) {
- cleanFieldName = fieldName.replace(/^c-/, '');
- }
-
- // 处理特殊情况
- if (cleanFieldName.indexOf('single-') === 0) {
- cleanFieldName = cleanFieldName.replace(/^single-/, '');
- }
-
- return fieldTabMap[cleanFieldName] || fieldTabMap[fieldName] || null;
- },
- showTabError: function(tabSelector, message) {
- var $tabLink = $('.nav-tabs a[href="' + tabSelector + '"]');
- if ($tabLink.length > 0) {
- $tabLink.addClass('tab-error');
- if ($tabLink.find('.error-icon').length === 0) {
- $tabLink.append(' <i class="fa fa-exclamation-circle error-icon"></i>');
- }
- if (message) {
- $tabLink.attr('title', message).tooltip('destroy').tooltip();
- }
- }
- },
- clearTabError: function(tabSelector) {
- var $tabLink = $('.nav-tabs a[href="' + tabSelector + '"]');
- if ($tabLink.length > 0) {
- $tabLink.removeClass('tab-error');
- $tabLink.find('.error-icon').remove();
- $tabLink.removeAttr('title').tooltip('destroy');
- }
- },
- clearTabErrors: function() {
- $('.nav-tabs a').removeClass('tab-error');
- $('.nav-tabs .error-icon').remove();
- $('.nav-tabs a').removeAttr('title').tooltip('destroy');
- },
- highlightErrorFields: function(tabId, errorFields) {
- if (!tabId) return;
-
- var $tabPane = $(tabId);
- if ($tabPane.length === 0) return;
-
- var $fieldsToHighlight = $();
-
- if (errorFields && errorFields.length > 0) {
- errorFields.forEach(function(element) {
- if (element) {
- $fieldsToHighlight = $fieldsToHighlight.add($(element));
- }
- });
- } else {
- // 使用FastAdmin默认的错误样式查找字段
- $fieldsToHighlight = $tabPane.find('.has-error input, .has-error select, .has-error textarea')
- .add($tabPane.find('.form-group.has-error input, .form-group.has-error select, .form-group.has-error textarea'))
- .add($tabPane.find('.is-invalid'))
- .add($tabPane.find('[aria-invalid="true"]'));
-
- if ($fieldsToHighlight.length === 0) {
- $fieldsToHighlight = $tabPane.find('.msg-box:visible').siblings('input, select, textarea');
- }
- }
-
- // 自动聚焦到第一个错误字段,不添加自定义样式
- if ($fieldsToHighlight.length > 0) {
- var $firstError = $fieldsToHighlight.first();
- setTimeout(function() {
- $firstError.focus();
- $('html, body').animate({
- scrollTop: $firstError.offset().top - 100
- }, 300);
- }, 100);
- }
- },
- handleValidationError: function(errorMessage, fieldName) {
- var targetTab = this.getTabByFieldName(fieldName);
- if (targetTab) {
- var stepIndex = this.steps.findIndex(function(step) {
- return step.id === targetTab;
- });
- if (stepIndex !== -1) {
- this.goToStep(stepIndex);
- setTimeout(function() {
- TabValidator.highlightErrorFields(targetTab);
- }, 300);
- }
- }
- }
- };
- var Controller = {
- index: function () {
- Table.api.init({
- extend: {
- index_url: 'shop/goods/index' + location.search,
- add_url: 'shop/goods/add',
- edit_url: 'shop/goods/edit?dialog=1',
- del_url: 'shop/goods/del',
- multi_url: 'shop/goods/multi',
- import_url: 'shop/goods/import',
- table: 'shop_goods',
- }
- });
-
- Table.config.dragsortfield = '';
- var table = $("#table");
- var goodsTypeList = Controller.api.parseConfigJson('goodsTypeList', {});
- var specTypeList = Controller.api.parseConfigJson('specTypeList', {});
- var statusList = Controller.api.parseConfigJson('statusList', {});
-
- table.bootstrapTable({
- url: $.fn.bootstrapTable.defaults.extend.index_url,
- pk: 'id',
- sortName: 'id',
- fixedColumns: true,
- fixedRightNumber: 1,
- columns: [
- [
- {checkbox: true},
- {field: 'id', title: 'ID', width: 60, sortable: true},
- {field: 'image', title: '图片', width: 100, operate: false, events: Table.api.events.image, formatter: Table.api.formatter.image},
- {
- field: 'title',
- title: '商品信息',
- width: 300,
- operate: 'LIKE',
- formatter: Controller.formatGoodsInfo
- },
- {
- field: 'type',
- title: '商品类型',
- width: 100,
- searchList: goodsTypeList,
- formatter: Controller.formatGoodsType
- },
- {
- field: 'spec_type',
- title: '规格',
- width: 80,
- searchList: specTypeList,
- formatter: Controller.formatSpecType
- },
- {field: 'price', title: '价格', width: 80, operate: 'BETWEEN', sortable: true},
- {field: 'stocks', title: '库存', width: 80, operate: 'BETWEEN', sortable: true},
- {field: 'sales', title: '销量', width: 80, operate: 'BETWEEN', sortable: true},
- {field: 'views', title: '浏览量', width: 80, operate: 'BETWEEN', sortable: true},
- {
- field: 'status',
- title: '状态',
- width: 80,
- searchList: statusList,
- formatter: Controller.formatGoodsStatus
- },
- {field: 'createtime', title: '创建时间', width: 120, operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime, visible: false},
- {field: 'updatetime', title: '更新时间', width: 120, operate: 'RANGE', addclass: 'datetimerange', autocomplete: false, formatter: Table.api.formatter.datetime, visible: false},
-
- {field: 'brand_id', title: '品牌', visible: false, searchList: $.getJSON("shop/brand/getList")},
- {field: 'goods_sn', title: '商品编码', visible: false, operate: 'LIKE'},
- {field: 'title', title: '商品标题', visible: false, operate: 'LIKE'},
-
- {
- field: 'operate',
- title: '操作',
- width: 120,
- table: table,
- events: Table.api.events.operate,
- formatter: Table.api.formatter.operate,
- buttons: [
- {
- name: 'edit',
- title: '编辑',
- classname: 'btn btn-xs btn-success btn-dialog',
- icon: 'fa fa-pencil',
- url: 'shop/goods/edit'
- }
- ]
- }
- ]
- ]
- });
- Table.api.bindevent(table);
- },
- formatGoodsInfo: function(value, row, index) {
- var html = '<div style="width: 280px; max-width: 280px; padding: 8px 0; line-height: 1.4; overflow: hidden; box-sizing: border-box;">';
-
- html += '<div style="width: 100%; max-width: 100%; font-weight: 600; color: #2c3e50; font-size: 14px; line-height: 1.3; margin-bottom: 5px; word-wrap: break-word; word-break: break-all; overflow-wrap: break-word; white-space: normal; overflow: hidden; box-sizing: border-box;" title="' + (value || '') + '">' + (value || '') + '</div>';
-
- html += '<div style="width: 100%; max-width: 100%; font-size: 11px; color: #666; margin-bottom: 3px; line-height: 1.2; overflow: hidden; box-sizing: border-box;">';
- html += '<span style="display: inline-block; margin-right: 8px; max-width: 120px; overflow: hidden;">';
- html += '<i class="fa fa-barcode" style="margin-right: 3px; color: #999;"></i>';
- html += '<span style="word-break: break-all; overflow-wrap: break-word;">' + (row.goods_sn || '-') + '</span>';
- html += '</span>';
-
- if (row.brand && row.brand.name) {
- html += '<span style="display: inline-block; max-width: 120px; overflow: hidden;">';
- html += '<i class="fa fa-tag" style="margin-right: 3px; color: #999;"></i>';
- html += '<span style="word-break: break-all; overflow-wrap: break-word;">' + row.brand.name + '</span>';
- html += '</span>';
- }
- html += '</div>';
-
- if (row.subtitle) {
- html += '<div style="width: 100%; max-width: 100%; font-size: 11px; color: #999; line-height: 1.3; margin-bottom: 3px; word-wrap: break-word; word-break: break-all; overflow-wrap: break-word; white-space: normal; overflow: hidden; box-sizing: border-box;" title="' + row.subtitle + '">' + row.subtitle + '</div>';
- }
-
- if (row.spec_type == 1 && row.default_sku && row.default_sku.sku_attr) {
- html += '<div style="width: 100%; max-width: 100%; font-size: 10px; color: #888; margin-top: 4px; padding: 2px 6px; background-color: #f5f5f5; border-radius: 3px; word-wrap: break-word; word-break: break-all; overflow-wrap: break-word; white-space: normal; overflow: hidden; box-sizing: border-box;">';
- html += '<i class="fa fa-list-alt" style="margin-right: 3px;"></i>';
- html += '默认规格: ' + Controller.formatSkuAttr(row.default_sku.sku_attr);
- html += '</div>';
- }
-
- if (row.activity_name && row.activity_name.trim() !== '') {
- html += '<div style="width: 100%; max-width: 100%; margin-top: 6px; overflow: hidden; box-sizing: border-box;">';
- html += '<span style="display: inline-block; font-size: 10px; color: #fff; background-color: #ff6b35; padding: 2px 6px; border-radius: 12px; margin-right: 4px; word-wrap: break-word; word-break: break-all; overflow-wrap: break-word; white-space: normal;">';
- html += '<i class="fa fa-fire" style="margin-right: 2px;"></i>限时折扣活动</span>';
- html += '<span style="font-size: 11px; color: #ff6b35; font-weight: 600; word-wrap: break-word; word-break: break-all; overflow-wrap: break-word; white-space: normal;">' + row.activity_name + '</span>';
- html += '</div>';
- }
-
- html += '</div>';
- return html;
- },
- formatGoodsType: function(value, row, index) {
- var goodsTypeList = Controller.api.parseConfigJson('goodsTypeList', {});
- var text = (goodsTypeList && goodsTypeList[value]) ? goodsTypeList[value] : '未知';
- var typeStyles = {
- 1: {class: 'label-primary', icon: 'fa-cube'},
- 2: {class: 'label-info', icon: 'fa-key'},
- 3: {class: 'label-warning', icon: 'fa-cloud'},
- 4: {class: 'label-success', icon: 'fa-ticket'}
- };
- var style = typeStyles[parseInt(value)] || {class: 'label-primary', icon: 'fa-cube'};
- return '<span class="label ' + style.class + '"><i class="fa ' + style.icon + '"></i> ' + text + '</span>';
- },
- formatSpecType: function(value, row, index) {
- var specTypeList = Controller.api.parseConfigJson('specTypeList', {});
- var label = (specTypeList && specTypeList[value]) ? specTypeList[value] : (value == 1 ? '多规格' : '单规格');
- var className = value == 1 ? 'label-info' : 'label-success';
- return '<span class="label ' + className + '"><i class="fa ' + (value == 1 ? 'fa-list' : 'fa-square') + '"></i> ' + label + '</span>';
- },
- formatGoodsStatus: function(value, row, index) {
- var statusList = Controller.api.parseConfigJson('statusList', {});
- var statusStyles = {
- 0: {class: 'label-warning', icon: 'fa-warehouse'},
- 1: {class: 'label-success', icon: 'fa-check-circle'},
- 2: {class: 'label-danger', icon: 'fa-times-circle'},
- 3: {class: 'label-default', icon: 'fa-eye-slash'}
- };
- var text = statusList && statusList[value] ? statusList[value] : '未知';
- var style = statusStyles[value] || {class: 'label-default', icon: 'fa-question'};
- return '<span class="label ' + style.class + '"><i class="fa ' + style.icon + '"></i> ' + text + '</span>';
- },
- select: function () {
- var specTypeList = Controller.api.parseConfigJson('specTypeList', {0: "单规格", 1: "多规格"});
-
- var urlParams = new URLSearchParams(window.location.search);
- var selectMode = urlParams.get('selectMode') || 'multiple';
- var maxSelect = parseInt(urlParams.get('maxSelect')) || 0;
-
- Table.api.init({
- extend: {
- index_url: 'shop/goods/index' + location.search,
- table: 'shop_goods',
- }
- });
- Table.config.dragsortfield = '';
- var table = $("#table");
- var columns = [
- {field: 'id', title: 'ID', width: 60},
- {field: 'image', title: '图片', width: 120, operate: false, events: Table.api.events.image, formatter: Table.api.formatter.image},
- {
- field: 'title',
- title: '商品信息',
- operate: 'LIKE',
- formatter: function (value, row, index) {
- var html = '<div>';
- html += '<div style="font-weight: bold; margin-bottom: 2px;">' + value + '</div>';
- html += '<div style="font-size: 12px; color: #999;">';
- html += '编码: ' + (row.goods_sn || '-');
- if (row.category && row.category.name) {
- html += ' | 分类: ' + row.category.name;
- }
- html += '</div>';
- html += '</div>';
- return html;
- }
- },
- {field: 'spec_type', title: '规格', width: 80, searchList: specTypeList, formatter: Table.api.formatter.label},
- {
- field: 'price',
- title: '价格',
- width: 100,
- operate: 'BETWEEN',
- formatter: function (value, row, index) {
- return '<span style="color: #e74c3c; font-weight: bold;">¥' + (value || '0.00') + '</span>';
- }
- },
- {field: 'stocks', title: '库存', width: 80},
-
- {
- field: 'operate',
- title: selectMode === 'single' ? '选择' : '操作',
- width: selectMode === 'single' ? 80 : 100,
- operate: false,
- formatter: function (value, row, index) {
- var html = '';
- if (selectMode === 'single') {
- html = '<button class="btn btn-xs btn-primary btn-select-single" data-id="' + row.id + '" data-index="' + index + '" title="点击选择该商品">' +
- '<i class="fa fa-check"></i> 选择</button>';
- } else {
- html = '<button class="btn btn-xs btn-success btn-select-multi" data-id="' + row.id + '" data-index="' + index + '" data-selected="false" title="点击添加到选择列表">' +
- '<i class="fa fa-plus"></i> 选择</button>';
- }
- return html;
- }
- },
-
- {field: 'category_id', title: '分类', visible: false, searchList: $.getJSON("shop/category/getList")},
- {field: 'goods_sn', title: '商品编码', visible: false, operate: 'LIKE'},
- ];
- table.bootstrapTable({
- url: $.fn.bootstrapTable.defaults.extend.index_url,
- pk: 'id',
- sortName: 'id',
- clickToSelect: false,
- columns: [columns]
- });
-
- var selectedGoods = [];
-
- $(document).on('click', '.btn-select-single', function () {
- var $btn = $(this);
- var index = $btn.data('index');
- var rowData = table.bootstrapTable('getData')[index];
- Fast.api.close([rowData]);
- });
-
- $(document).on('click', '.btn-select-multi', function () {
- var $btn = $(this);
- var id = $btn.data('id');
- var index = $btn.data('index');
- var isSelected = $btn.data('selected');
- var rowData = table.bootstrapTable('getData')[index];
-
- if (!isSelected) {
- if (maxSelect > 0 && selectedGoods.length >= maxSelect) {
- Toastr.warning('最多只能选择 ' + maxSelect + ' 个商品');
- return;
- }
-
- selectedGoods.push(rowData);
- $btn.removeClass('btn-success').addClass('btn-danger')
- .html('<i class="fa fa-minus"></i> 取消选择')
- .attr('title', '点击从选择列表中移除')
- .data('selected', true);
- } else {
- selectedGoods = selectedGoods.filter(function(item) {
- return item.id != id;
- });
- $btn.removeClass('btn-danger').addClass('btn-success')
- .html('<i class="fa fa-plus"></i> 选择')
- .attr('title', '点击添加到选择列表')
- .data('selected', false);
- }
-
- updateSelectedCount();
- });
-
- function updateSelectedCount() {
- var count = selectedGoods.length;
- var countText = count > 0 ? '已选择 ' + count + ' 个商品' : '请选择商品';
- $('.btn-goods-select').text(countText === '请选择商品' ? '确认选择' : '确认选择 (' + count + ')');
-
- if (maxSelect > 0 && count >= maxSelect) {
- $('.btn-select-multi[data-selected="false"]').prop('disabled', true).addClass('disabled');
- } else {
- $('.btn-select-multi[data-selected="false"]').prop('disabled', false).removeClass('disabled');
- }
- }
-
- $(document).on('click', '.btn-goods-select', function () {
- if (selectedGoods.length === 0) {
- Layer.alert('请选择商品');
- return;
- }
- Fast.api.close(selectedGoods);
- });
-
- Table.api.bindevent(table);
- },
- recyclebin: function () {
- Table.api.init({
- extend: {
- 'dragsort_url': ''
- }
- });
- var table = $("#table");
- table.bootstrapTable({
- url: 'shop/goods/recyclebin' + location.search,
- pk: 'id',
- sortName: 'id',
- columns: [
- [{
- checkbox: true
- },
- {
- field: 'id',
- title: __('Id')
- },
- {
- field: 'title',
- title: __('Title'),
- align: 'left'
- },
- {
- field: 'deletetime',
- title: __('Deletetime'),
- operate: 'RANGE',
- addclass: 'datetimerange',
- formatter: Table.api.formatter.datetime
- },
- {
- field: 'operate',
- width: '130px',
- title: __('Operate'),
- table: table,
- events: Table.api.events.operate,
- buttons: [{
- name: 'Restore',
- text: __('Restore'),
- classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
- icon: 'fa fa-rotate-left',
- url: 'shop/goods/restore',
- refresh: true
- },
- {
- name: 'Destroy',
- text: __('Destroy'),
- classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
- icon: 'fa fa-times',
- url: 'shop/goods/destroy',
- refresh: true
- }
- ],
- formatter: Table.api.formatter.operate
- }
- ]
- ]
- });
- Table.api.bindevent(table);
- },
- getAttribute: function (category_id, attribute_ids = []) {
- $.get('shop/attribute/attrs?category_id=' + category_id, function (res) {
- const {code, data, msg} = res;
- if (code) {
- $('#attributes').html(data.length ? Template('attributetpl', {row: data, attribute_ids}) : ' <input name="row[attribute_ids][0][]" type="hidden" value="">');
- var instance = $("form[data-toggle='validator']").data("validator");
- $('#attributes input').each(function () {
- instance._parse(this);
- });
- } else {
- Toastr.error(msg);
- }
- })
- },
- add: function () {
- var that = this;
- $(document).ready(function() {
- that.initializeForm();
- Controller.api.bindevent();
- Controller.api.add_sku();
- });
- },
-
- edit: function () {
- var that = this;
- $(document).ready(function() {
- that.initializeForm(true);
- that.initEditTypeState();
- setTimeout(function() {
- that.initSingleSpecData();
- }, 1000);
- Controller.api.bindevent();
- Controller.api.add_sku();
- });
- },
- initializeForm: function(isEdit) {
- var that = this;
-
- that.initGoodsTypeCards();
- that.initOnlineOfflineControl();
- that.initContentPreview();
- TabValidator.init();
- that.watchSingleSpecFormVisibility();
-
- if (isEdit) {
- $('#c-category_id, #c-category_ids').on('change', function () {
- var category_id = $(this).val();
- });
- }
- },
- watchSingleSpecFormVisibility: function() {
- var that = this;
-
- if (typeof MutationObserver !== 'undefined') {
- var observer = new MutationObserver(function(mutations) {
- mutations.forEach(function(mutation) {
- var $singleSpecForm = $('#single-spec-form');
- if ($singleSpecForm.length > 0 && $singleSpecForm.is(':visible')) {
- var $priceField = $('#c-price');
- var $stocksField = $('#c-stocks');
-
- if ($priceField.length > 0 && $stocksField.length > 0) {
- var currentPrice = $priceField.val();
- var currentStocks = $stocksField.val();
-
- if ((!currentPrice || currentPrice === '') &&
- (!currentStocks || currentStocks === '') &&
- Config.goods && Config.goods.spec_type == '0' &&
- Config.goods_skus && Config.goods_skus.length > 0) {
-
- setTimeout(function() {
- that.initSingleSpecData();
- }, 100);
- }
- }
- }
- });
- });
-
- observer.observe(document.body, {
- childList: true,
- subtree: true,
- attributes: true,
- attributeFilter: ['style', 'class']
- });
-
- this._singleSpecObserver = observer;
- }
-
- $('a[href="#skus"]').on('shown.bs.tab', function() {
- setTimeout(function() {
- var specType = $('input[name="row[spec_type]"]:checked').val();
- if (specType == '0') {
- var $priceField = $('#c-price');
- var $stocksField = $('#c-stocks');
-
- if ($priceField.length > 0 && $stocksField.length > 0) {
- var currentPrice = $priceField.val();
- var currentStocks = $stocksField.val();
-
- if ((!currentPrice || currentPrice === '') &&
- (!currentStocks || currentStocks === '')) {
- that.initSingleSpecData();
- }
- }
- }
- }, 200);
- });
- },
- isMultiSelectEmpty: function(value) {
- if (Array.isArray(value)) {
- return value.length === 0;
- } else if (typeof value === 'string') {
- var trimmed = value.trim();
- return trimmed === '' || trimmed === '0' || trimmed === 'null' || trimmed === 'undefined';
- } else {
- return !value || value === null || value === undefined;
- }
- },
- initGoodsTypeCards: function() {
- if ($('.goods-type-card').length === 0) {
- return;
- }
-
- function updateTypeCardState() {
- var $cards = $('.goods-type-card');
- if ($cards.length > 0) {
- $cards.removeClass('selected');
- $('.goods-type-card input[type="radio"]:checked').closest('.goods-type-card').addClass('selected');
- }
- }
-
- var $radioInputs = $('.goods-type-card input[type="radio"]');
- if ($radioInputs && $radioInputs.length > 0) {
- $radioInputs.off('change.typecard').on('change.typecard', function() {
- updateTypeCardState();
- });
- }
-
- updateTypeCardState();
- },
- initEditTypeState: function() {
- if (Config.goods && Config.goods.type) {
- var $typeRadio = $('input[name="row[type]"][value="' + Config.goods.type + '"]');
- if ($typeRadio.length > 0) {
- $typeRadio.prop('checked', true);
- $typeRadio.trigger('change');
- }
- }
-
- if (Config.goods && Config.goods.online_type !== undefined) {
- var $onlineTypeRadio = $('input[name="row[online_type]"][value="' + Config.goods.online_type + '"]');
- if ($onlineTypeRadio.length > 0) {
- $onlineTypeRadio.prop('checked', true);
- $onlineTypeRadio.trigger('change');
- }
- }
-
- if (Config.goods) {
- if (Config.goods.scheduled_online_time) {
- $('#c-scheduled_online_time').val(Config.goods.scheduled_online_time);
- }
-
- if (Config.goods.is_auto_offline) {
- $('#c-is_auto_offline').prop('checked', true);
- if (Config.goods.scheduled_offline_time) {
- $('#c-scheduled_offline_time').val(Config.goods.scheduled_offline_time);
- }
- }
- }
-
- if (Config.goods && Config.goods.spec_type !== undefined) {
- var $specTypeRadio = $('input[name="row[spec_type]"][value="' + Config.goods.spec_type + '"]');
- if ($specTypeRadio.length > 0) {
- $specTypeRadio.prop('checked', true);
- $specTypeRadio.trigger('change');
-
- setTimeout(function() {
- $(document).trigger('fa.event.favisible', $specTypeRadio);
- }, 100);
- }
- }
-
- this.initSingleSpecData();
- },
- initSingleSpecData: function() {
- var that = this;
-
- if (Config.goods && Config.goods.spec_type == '0' && Config.goods_skus && Config.goods_skus.length > 0) {
- var sku = Config.goods_skus[0];
- var fillAttempts = 0;
- var maxAttempts = 5;
-
- function attemptFill() {
- fillAttempts++;
- var $priceField = $('#c-price');
- var $stocksField = $('#c-stocks');
-
- if ($priceField.length === 0 || $stocksField.length === 0) {
- if (fillAttempts < maxAttempts) {
- setTimeout(attemptFill, 200 * fillAttempts);
- }
- return;
- }
-
- try {
- $priceField.val(sku.price || '0.01');
- $('#c-lineation_price').val(sku.lineation_price || '0.00');
- $('#c-cost_price').val(sku.cost_price || '0.00');
- $stocksField.val(sku.stocks || '1');
- $('#c-weight').val(sku.weight || '0.00');
- $('#c-volume').val(sku.volume || '0.00');
- $('#c-sku-sn').val(sku.sku_sn || '');
- $('#c-single-image').val(sku.image || '');
-
- if (sku.image) {
- var $preview = $('#p-single-image');
- if ($preview.length > 0) {
- var img = '<li><a href="' + Fast.api.cdnurl(sku.image, true) + '" data-url="' + sku.image + '" target="_blank"><img src="' + Fast.api.cdnurl(sku.image, true) + '"/></a></li>';
- $preview.html(img);
- }
- }
-
- $priceField.trigger('change');
- $stocksField.trigger('change');
-
- setTimeout(function() {
- var currentPrice = $priceField.val();
- var currentStocks = $stocksField.val();
-
- if (!currentPrice || currentPrice === '' || !currentStocks || currentStocks === '') {
- if (fillAttempts < maxAttempts) {
- setTimeout(attemptFill, 300);
- }
- }
- }, 100);
-
- } catch (error) {
- if (fillAttempts < maxAttempts) {
- setTimeout(attemptFill, 200 * fillAttempts);
- }
- }
- }
-
- setTimeout(attemptFill, 300);
- }
- },
- validateField: function(fieldName) {
- var $field = $('[name="' + fieldName + '"]');
- var result = {
- isValid: true,
- label: fieldName,
- element: null,
- message: ''
- };
-
- if ($field.length > 0) {
- result.element = $field[0];
- var $formGroup = $field.closest('.form-group');
- var $label = $formGroup.find('.control-label');
-
- if ($label.length > 0) {
- result.label = $label.text().replace(/[::*]/g, '').trim();
- }
-
- // 使用 FastAdmin 的 Nice-validator 进行验证
- var $form = $field.closest('form');
- var validator = $form.data('validator');
-
- if (validator && typeof validator.isValid === 'function') {
- // 触发单个字段验证
- var fieldResult = validator.isValid($field[0]);
- if (!fieldResult) {
- result.isValid = false;
- // 获取错误消息
- var $msgBox = $field.closest('.form-group').find('.msg-box');
- if ($msgBox.length > 0) {
- result.message = $msgBox.text() || result.label + '验证失败';
- } else {
- result.message = result.label + '验证失败';
- }
- }
- } else {
- // FastAdmin 的 Nice-validator 可能尚未初始化,使用基础检查
- var value = $field.val();
- var rules = $field.attr('data-rule');
- if (rules && rules.indexOf('required') !== -1) {
- if (!value || value.trim() === '') {
- result.isValid = false;
- result.message = result.label + '不能为空';
- }
- }
- }
- }
-
- return result;
- },
- validatePriceStock: function() {
- var result = {
- isValid: true,
- errors: [],
- errorFields: []
- };
-
- var specType = $('input[name="row[spec_type]"]:checked').val();
-
- if (specType == '0') {
- var price = $('#c-price').val();
- var stocks = $('#c-stocks').val();
-
- if (!price || parseFloat(price) <= 0) {
- result.isValid = false;
- result.errors.push('销售价格');
- result.errorFields.push(document.getElementById('c-price'));
- }
- if (!stocks || parseInt(stocks) <= 0) {
- result.isValid = false;
- result.errors.push('库存数量');
- result.errorFields.push(document.getElementById('c-stocks'));
- }
- } else if (specType == '1') {
- if (typeof vm !== 'undefined' && vm && vm.tableData && vm.tableData.length > 0) {
- var validSkus = vm.tableData.filter(function(sku) {
- return sku.status == 1 && sku.price > 0 && sku.stocks > 0;
- });
-
- if (validSkus.length === 0) {
- result.isValid = false;
- result.errors.push('至少需要一个有效的SKU规格(价格>0,库存>0,且状态为显示)');
- }
- } else {
- result.isValid = false;
- result.errors.push('请先设置商品规格');
- }
- }
-
- return result;
- },
- initOnlineOfflineControl: function() {
- $('input[name="row[online_type]"]').change(function() {
- var value = $(this).val();
- if (value == '3') {
- $('#scheduled-online-time').show();
- } else {
- $('#scheduled-online-time').hide();
- }
-
- if (value == '1' || value == '3') {
- $('#offline-time-setting').show();
- } else {
- $('#offline-time-setting').hide();
- }
- });
-
- $('input[name="row[online_type]"]:checked').trigger('change');
-
- $('#c-is_auto_offline').change(function() {
- if ($(this).is(':checked')) {
- $('#scheduled-offline-time').show();
- } else {
- $('#scheduled-offline-time').hide();
- }
- });
-
- if ($('#c-is_auto_offline').is(':checked')) {
- $('#scheduled-offline-time').show();
- } else {
- $('#scheduled-offline-time').hide();
- }
- },
- initContentPreview: function() {
- var previewTimer = null;
- var lastContent = "";
-
- $('a[href="#detail"]').on('shown.bs.tab', function (e) {
- updatePreview();
-
- if (previewTimer === null) {
- previewTimer = setInterval(function() {
- checkContentChange();
- }, 1000);
- }
- });
-
- $('a').not('a[href="#detail"]').on('shown.bs.tab', function (e) {
- if (previewTimer) {
- clearInterval(previewTimer);
- previewTimer = null;
- }
- });
-
- function checkContentChange() {
- var editor = $("#c-content").data("nkeditor");
- if (!editor) return;
-
- var content = editor.html();
- if (content !== lastContent) {
- lastContent = content;
- updatePreview();
- }
- }
-
- function updatePreview() {
- var editor = $("#c-content").data("nkeditor");
- if (!editor) return;
-
- var content = editor.html();
- $('#content-preview').html(content || '<div class="text-center text-muted">暂无内容</div>');
- adjustPreviewHeight();
- }
-
- function adjustPreviewHeight() {
- var $editorContainer = $('.ke-container');
- if ($editorContainer.length > 0) {
- var editorHeight = $editorContainer.height();
- $('#content-preview').css('height', editorHeight - 40 + 'px');
- }
- }
-
- setTimeout(function() {
- adjustPreviewHeight();
- updatePreview();
- }, 1000);
- },
- // 解析sku_attr数据的通用方法
- parseSkuAttr: function(skuAttr) {
- if (!skuAttr) {
- return [];
- }
-
- try {
- if (skuAttr.charAt(0) === '[' || skuAttr.charAt(0) === '{') {
- // JSON格式
- return JSON.parse(skuAttr);
- } else {
- // 字符串格式:规格名:规格值,规格名:规格值
- var attrs = skuAttr.split(',');
- var result = [];
- attrs.forEach(function(attr) {
- var parts = attr.split(':');
- if (parts.length >= 2) {
- result.push({
- name: parts[0].trim(),
- key: parts[0].trim(),
- value: parts[1].trim(),
- type: 'basic'
- });
- }
- });
- return result;
- }
- } catch (e) {
- console.warn('解析sku_attr失败:', skuAttr, e);
- return [];
- }
- },
- // 生成sku_attr数据
- generateSkuAttr: function(specValues) {
- if (!specValues || !Array.isArray(specValues)) {
- return '';
- }
-
- var attrs = [];
- specValues.forEach(function(value) {
- if (value.name && value.value) {
- attrs.push(value.name + ':' + value.value);
- }
- });
- return attrs.join(',');
- },
- formatSkuAttr: function(skuAttr) {
- if (!skuAttr) {
- return '';
- }
-
- var attrs = this.parseSkuAttr(skuAttr);
- if (attrs.length > 0) {
- var formatted = [];
- attrs.forEach(function(attr) {
- if (attr.name && attr.value) {
- formatted.push(attr.name + ': ' + attr.value);
- } else if (attr.key && attr.value) {
- formatted.push(attr.key + ': ' + attr.value);
- }
- });
- return formatted.join(' | ');
- }
-
- return skuAttr;
- },
- api: {
- parseConfigJson: function(configKey, defaultValue) {
- var configValue = Config[configKey] || defaultValue || {};
-
- if (typeof configValue === 'string') {
- try {
- return JSON.parse(configValue);
- } catch (e) {
- return defaultValue || {};
- }
- }
-
- return configValue;
- },
- initSpecTypeControl: function() {
- $('input[name="row[spec_type]"]').off('change.spectype').on('change.spectype', function() {
- var specType = $(this).val();
-
- $(document).trigger('fa.event.favisible');
-
- if (specType == '0') {
- if (typeof vm !== 'undefined' && vm) {
- // vm.specList = [];
- // vm.tableData = [];
- }
-
- setTimeout(function() {
- Controller.initSingleSpecData();
- }, 200);
- }
-
- if (specType == '1' && typeof vm !== 'undefined' && vm) {
- var $multiSpecForm = $('#multi-spec-form');
- if ($multiSpecForm.length > 0 && !$multiSpecForm.is(':visible')) {
- setTimeout(function() {
- $(document).trigger('fa.event.favisible');
- }, 100);
- }
- }
- });
-
- $('input[name="row[spec_type]"]:checked').trigger('change.spectype');
- },
-
- bindevent: function () {
- $('[data-toggle="popover"]').popover({
- trigger: 'hover',
- html: true,
- container: 'body'
- });
- $(document).on("click", ".btn-legal", function (a) {
- Fast.api.ajax({
- url: "shop/ajax/check_content_islegal",
- data: {content: $("#c-content").val()}
- }, function (data, ret) {
- }, function (data, ret) {
- if ($.isArray(data)) {
- Layer.alert(__('Banned words') + ":" + data.join(","));
- }
- });
- });
- $(document).on("click", ".btn-keywords", function (a) {
- Fast.api.ajax({
- url: "shop/ajax/get_content_keywords",
- data: {title: $("#c-title").val(), content: $("#c-content").val()}
- }, function (data, ret) {
- $("#c-keywords").val(data.keywords);
- $("#c-description").val(data.description);
- });
- });
- var $form = $("form[role=form]");
-
- Form.api.bindevent($form, function (data, ret) {
- TabValidator.clearTabErrors();
- }, function (data, ret) {
- var errorMessage = ret.msg || '提交失败';
- var fieldName = null;
- var extraData = null;
-
- if (ret.data && typeof ret.data === 'object') {
- if (ret.data.errors) {
- extraData = { errors: ret.data.errors };
- } else if (ret.data.field) {
- fieldName = ret.data.field;
- }
- }
-
- if (!fieldName && errorMessage) {
- var fieldPatterns = [
- /(\w+)\s*(字段|不能|必须|不可)/,
- /row\[(\w+)\]/,
- /'(\w+)'\s*(字段|必须|不能)/,
- /(\w+)\s*is\s*(required|invalid)/,
- /请输入\s*(\w+)/,
- /(\w+)\s*格式不正确/
- ];
-
- for (var i = 0; i < fieldPatterns.length; i++) {
- var match = errorMessage.match(fieldPatterns[i]);
- if (match) {
- fieldName = match[1];
- break;
- }
- }
- }
-
- if (fieldName) {
- var targetTab = TabValidator.getTabByFieldName(fieldName);
- if (targetTab) {
- var steps = [
- { id: '#basics' }, { id: '#skus' }, { id: '#delivery' },
- { id: '#detail' }, { id: '#params' }, { id: '#sales' }
- ];
- var stepIndex = steps.findIndex(function(step) {
- return step.id === targetTab;
- });
-
- if (stepIndex !== -1) {
- TabValidator.goToStep(stepIndex);
-
- setTimeout(function() {
- TabValidator.highlightErrorFields(targetTab);
- }, 300);
- }
- }
- }
-
- TabValidator.handleValidationError(errorMessage, fieldName, extraData);
- Toastr.error(errorMessage);
- }, function (success, error) {
- TabValidator.clearTabErrors();
-
- if (typeof TabValidator.validateAllSteps === 'function') {
- var allValidationPassed = TabValidator.validateAllSteps();
- if (!allValidationPassed.isValid) {
-
- if (allValidationPassed.firstErrorStep !== -1) {
-
- TabValidator.goToStep(allValidationPassed.firstErrorStep);
-
- var errorMessages = [];
- for (var stepIndex in allValidationPassed.allErrors) {
- var stepError = allValidationPassed.allErrors[stepIndex];
- errorMessages.push(stepError.stepName + ': ' + stepError.errors.join('、'));
- }
-
- if (errorMessages.length > 0) {
- Toastr.error('请完善以下内容:\n' + errorMessages.join('\n'));
- }
- }
-
- return false;
- }
- }
-
- let skus = '[]', spec = '[]';
- var specType = $('input[name="row[spec_type]"]:checked').val();
-
- if (specType == '1') {
- if (typeof vm !== 'undefined' && vm && vm.tableData && vm.tableData.length > 0) {
- try {
- // 为每个SKU生成sku_attr字段
- var processedSkus = vm.tableData.map(function(sku, index) {
- var skuData = Object.assign({}, sku);
-
- // 生成sku_attr字段
- if (sku.skus && Array.isArray(sku.skus) && vm.specList) {
- var skuAttrs = [];
- sku.skus.forEach(function(specValue, specIndex) {
- if (vm.specList[specIndex] && specValue) {
- skuAttrs.push(vm.specList[specIndex].name + ':' + specValue);
- }
- });
- skuData.sku_attr = skuAttrs.join(',');
- } else {
- skuData.sku_attr = '';
- }
-
- return skuData;
- });
-
- skus = JSON.stringify(processedSkus);
- spec = JSON.stringify(vm.specList || []);
- } catch (e) {
- console.error('多规格数据处理失败:', e);
- Toastr.error('多规格数据处理失败');
- return false;
- }
- } else {
- Toastr.error('请设置完整的多规格数据');
- return false;
- }
- } else {
- var singleSku = {
- sku_sn: $('#c-sku-sn').val() || $('#c-goods_sn').val() || '',
- price: parseFloat($('#c-price').val()) || 0,
- lineation_price: parseFloat($('#c-lineation_price').val()) || 0,
- cost_price: parseFloat($('#c-cost_price').val()) || 0,
- stocks: parseInt($('#c-stocks').val()) || 0,
- weight: parseFloat($('#c-weight').val()) || 0,
- volume: parseFloat($('#c-volume').val()) || 0,
- image: $('#c-single-image').val() || '',
- sales: 0,
- status: 1,
- is_default: 1,
- sku_attr: '', // 单规格没有sku_attr
- skus: []
- };
- skus = JSON.stringify([singleSku]);
- spec = JSON.stringify([]);
- }
-
- let html = `<textarea id="c-skus" class="form-control hide" rows="5" name="row[skus]" cols="50">${skus}</textarea>
- <textarea id="c-spec" class="form-control hide" rows="5" name="row[spec]" cols="50">${spec}</textarea>`;
- this.find('#goods-sku').html(html);
- Form.api.submit(this, success, error);
- return false;
- });
- require(['backend/shop/card'], function (Card) {
- Card.api.bindcardevent();
- });
- },
- bindUpload: function () {
- if ($('.goods-sku-table table td.td-img button.faupload:not([initialized])').length === 0) {
- return;
- }
- clearTimeout(si);
- si = setTimeout(function () {
- let doms = $('.goods-sku-table table td.td-img').toArray();
- function uploadButtonTask(deadline) {
- while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && doms.length > 0) {
- bindEvent();
- }
- if (doms.length > 0)
- requestIdleCallback(uploadButtonTask);
- }
- function bindEvent() {
- var dom = doms.shift();
- if (dom) {
- var $elements = $(".faselect,.fachoose", dom);
- if ($elements && $elements.length > 0) {
- $elements.off('click');
- }
- Form.events.plupload(dom);
- Form.events.faselect(dom);
- }
- }
- requestIdleCallback(uploadButtonTask, {timeout: 1000});
- }, 250);
- },
- add_sku: function () {
- $('.td-img').find('.faupload').removeAttr('initialized');
-
- require(['vue'], function (Vue) {
- vm = new Vue({
- el: '#vue-app',
- computed: {
- specValueText() {
- return (skus, k) => {
- return !skus || typeof skus[k] == 'undefined' ? '' : skus[k];
- }
- },
- contentHtml() {
- return field => {
- return `<div class='input-group'>
- <input class='form-control' type='text' value=''/>
- <div class='input-group-btn'>
- <span class='btn btn-success sku-confirm' data-field='${field}'>
- 确定
- </span>
- </div>
- </div>`;
- }
- }
- },
- watch: {
- specList: {
- handler: function (val) {
- this.renderTableData(val);
- var that = this;
- that.$nextTick(function() {
- Controller.api.bindSpecValueUpload();
- Controller.api.rebindMultipleEdit();
- setTimeout(function() {
- Controller.api.bindUploadButtons();
- that.updateSpecImagePreviews();
- }, 300);
- });
- },
- deep: true
- }
- },
- data() {
- return {
- spec_name: '',
- specList: [],
- tableData: [],
- result: [],
- skus: [],
- defaultSpecIndex: 0
- }
- },
- mounted() {
- let that = this;
- that.init();
-
- setTimeout(function() {
- Controller.api.initSpecTypeControl();
- Controller.api.bindSpecValueUpload();
-
- if (Config.goods && Config.goods.spec_type == '1') {
- var $multiSpecForm = $('#multi-spec-form');
- if ($multiSpecForm.length > 0) {
- $multiSpecForm.show();
- }
- }
- }, 100);
-
- this.$nextTick(function () {
- $('body').on('click', function (e) {
- if (!$(e.target).hasClass('multiple-edit') &&
- $(e.target).parents('.multiple-edit').length === 0 &&
- $(e.target).parents('.popover.in').length === 0) {
- $('.multiple-edit').popover('hide');
- }
- });
- $(".multiple-edit").popover({
- sanitize: false,
- container: "body",
- html: true,
- placement: "top",
- trigger: 'manual'
- }).on('click', function (e) {
- $(".popover").hide();
- $(this).popover('show');
- });
- $(document).on('click', '.sku-confirm', function () {
- let value = $(this).parent().prev().val().trim();
- let field = $(this).data('field');
- if (field != 'sku_sn' && Number.isNaN(parseFloat(value))) {
- Toastr.error('请输入数字');
- return;
- }
- for (let [index] of that.tableData.entries()) {
- that.$set(that.tableData[index], field, value);
- }
- });
- require(['selectpage'], function () {
- $('.selectpage', $('.spec-template')).selectPage({
- eAjaxSuccess: function (data) {
- data.totalRow = data.total;
- return data;
- },
- eSelect: function (row) {
- let spec_names = row.spec_names.split(';');
- let spec_values = row.spec_values.split(';');
- let list = [];
- for (let [i, v] of spec_names.entries()) {
- let valueList = spec_values[i].split(',').map(val => ({
- name: val,
- image: '',
- description: ''
- }));
-
- list.push({
- name: v,
- type: 'basic',
- value: valueList
- });
- }
- that.tableData = [];
- that.skus = [];
- setTimeout(function () {
- that.specList = list;
- }, 100);
- }
- });
- });
- })
- },
- methods: {
- init() {
- let skus = [];
- if (Config.goods_skus && Config.goods_skus.length) {
-
- if (Config.goods && Config.goods.spec_type == '1') {
- let specList = [];
-
- if (Config.spec_data && Config.spec_data.length > 0) {
- specList = Config.spec_data.map(spec => {
- return {
- name: spec.name,
- type: spec.type || 'basic',
- value: spec.value || []
- };
- });
-
- for (let item of Config.goods_skus) {
- if (item.sku_attr) {
- let attr = [];
- let skuAttrs = [];
-
- // 使用统一的sku_attr解析方法
- skuAttrs = Controller.parseSkuAttr(item.sku_attr);
-
- for (let attrObj of skuAttrs) {
- let specName_key = attrObj.key || attrObj.name;
- let specValue = attrObj.value;
- if (specName_key && specValue) {
- attr.push(specValue);
- }
- }
-
- let attrKey = attr.join(',');
- skus[attrKey] = item;
- }
- }
-
- } else {
- let specName = {};
- let specTypeMap = {};
-
- for (let item of Config.goods_skus) {
- if (item.sku_attr) {
- let attr = [];
- let skuAttrs = [];
-
- // 使用统一的sku_attr解析方法
- skuAttrs = Controller.parseSkuAttr(item.sku_attr);
-
- for (let attrObj of skuAttrs) {
- let specName_key = attrObj.key || attrObj.name;
- let specValue = attrObj.value;
- let specType = attrObj.type || 'basic';
-
- if (specName_key && specValue) {
- attr.push(specValue);
- if (!specName[specName_key]) {
- specName[specName_key] = [];
- }
- if (!specName[specName_key].includes(specValue)) {
- specName[specName_key].push(specValue);
- }
- specTypeMap[specName_key] = specType;
- }
- }
- let attrKey = attr.join(',');
- skus[attrKey] = item;
- }
- }
-
- let specValueMap = {};
- if (Config.spec_values && Config.spec_values.length > 0) {
- Config.spec_values.forEach(function(item) {
- if (!specValueMap[item.spec_name]) {
- specValueMap[item.spec_name] = {};
- }
- specValueMap[item.spec_name][item.value] = {
- name: item.value,
- image: item.image || '',
- description: item.description || ''
- };
- });
- }
-
- for (let i in specName) {
- let valueList = specName[i].map(val => {
- if (typeof val === 'string') {
- if (specValueMap[i] && specValueMap[i][val]) {
- return {
- name: specValueMap[i][val].name,
- image: specValueMap[i][val].image || '',
- description: specValueMap[i][val].description || ''
- };
- } else {
- return {
- name: val,
- image: '',
- description: ''
- };
- }
- }
- return {
- name: val.name || val,
- image: val.image || '',
- description: val.description || ''
- };
- });
-
- specList.push({
- name: i,
- type: specTypeMap[i] || 'basic',
- value: valueList
- });
- }
- }
-
- this.skus = skus;
- this.specList = specList;
-
- this.$nextTick(() => {
- if (this.specList.length > 0) {
- this.renderTableData(this.specList);
-
- setTimeout(() => {
- this.updateSpecImagePreviews();
- }, 500);
- }
- });
- }
- }
-
- },
- addSpec() {
- if (!this.spec_name.trim()) {
- Toastr.error('请输入规格名称');
- return;
- }
- if (this.specList.some(item => item.name == this.spec_name)) {
- Toastr.error('已存在规格名称');
- return;
- }
- this.specList.push({
- name: this.spec_name,
- type: 'basic',
- value: [{
- name: '',
- image: '',
- description: ''
- }]
- });
- this.spec_name = '';
- },
- showAddSpecForm() {
- this.specList.push({
- name: '',
- type: 'basic',
- value: [{
- name: '',
- image: '',
- description: ''
- }]
- });
- },
- addSpecValue(key) {
- this.specList[key].value.push({
- name: '',
- image: '',
- description: ''
- });
-
- this.$nextTick(function() {
- setTimeout(function() {
- Controller.api.bindUploadButtons();
- }, 100);
- });
- },
- removeSpecValue(key, index) {
- this.specList[key].value.splice(index, 1);
- },
- renderTableData(list) {
- const isEditMode = Config.goods && Config.goods.id;
-
- const defaultValues = {
- goods_sn: $('#c-goods_sn').val() || '',
- price: '0.01',
- lineation_price: '0.00',
- cost_price: '0.00',
- weight: '0.00',
- volume: '0.00',
- stocks: '1'
- };
-
- let columns = [];
- this.result = [];
- this.resetSpec(list, 0);
-
- this.result.forEach((item, index) => {
- let su = this.skus[item];
-
- let row;
-
- if (isEditMode && su) {
- row = {
- skus: item ? item.split(',') : [],
- sku_sn: su.sku_sn || '',
- image: su.image || '',
- price: parseFloat(su.price) || 0,
- lineation_price: parseFloat(su.lineation_price) || 0,
- cost_price: parseFloat(su.cost_price) || 0,
- weight: parseFloat(su.weight) || 0,
- volume: parseFloat(su.volume) || 0,
- stocks: parseInt(su.stocks) || 0,
- sales: parseInt(su.sales) || 0,
- status: su.status !== undefined ? parseInt(su.status) : 1,
- is_default: su.is_default !== undefined ? parseInt(su.is_default) : 0
- };
- } else {
- row = {
- skus: item ? item.split(',') : [],
- sku_sn: defaultValues.goods_sn,
- image: '',
- price: defaultValues.price,
- lineation_price: defaultValues.lineation_price,
- cost_price: defaultValues.cost_price,
- weight: defaultValues.weight,
- volume: defaultValues.volume,
- stocks: defaultValues.stocks,
- sales: 0,
- status: 1,
- is_default: index === 0 ? 1 : 0
- };
- }
-
- if (!isEditMode) {
- let old = this.tableData[index];
- if (old) {
- for (let i in row) {
- if ((row[i] === '' || row[i] === 0 || row[i] === '0.00') && old[i]) {
- row[i] = old[i];
- }
- }
- }
- }
-
- columns.push(row);
- });
-
- this.tableData = columns;
-
- if (isEditMode) {
- this.defaultSpecIndex = this.tableData.findIndex(item => item.is_default === 1);
- }
-
- this.$nextTick(function () {
- Controller.api.bindUpload();
- Controller.api.bindSpecValueUpload();
- Controller.api.rebindMultipleEdit();
- });
- },
- removeSpec(key) {
- this.specList.splice(key, 1);
- },
-
- updateSpecImagePreviews() {
- this.specList.forEach((spec, specKey) => {
- if (spec.value && spec.value.length > 0) {
- spec.value.forEach((value, valueIndex) => {
- if (value.image) {
- const previewId = 'p-spec-image-' + specKey + '-' + valueIndex;
- const $preview = $('#' + previewId);
- if ($preview.length > 0) {
- const img = '<img src="' + Fast.api.cdnurl(value.image) + '" style="width:40px;height:40px;object-fit:cover;border-radius:4px;border:1px solid #ddd;">';
- $preview.html(img);
- }
- }
- });
- }
- });
- },
- resetSpec(list, index) {
- if (list[index] != undefined) {
- let value = list[index].value.map(item => typeof item === 'string' ? item : item.name);
- if (!index) {
- this.result = value;
- } else {
- let res = [];
- for (let i of this.result) {
- for (let j of value) {
- res.push(i + ',' + j);
- }
- }
- if (res.length) {
- this.result = res;
- }
- }
- this.resetSpec(list, ++index);
- }
- },
- }
- });
- });
- $(document).on('click', '.btn-del-sku', function () {
- vm.specList = [];
- vm.tableData = [];
- });
- $(document).on('change', '.sku-images', function () {
- let index = $(this).data('index');
- let value = $(this).val();
- vm.tableData[index].image = value;
- });
- },
-
- rebindMultipleEdit: function() {
- var $multipleEdit = $(".multiple-edit");
- if (!$multipleEdit || $multipleEdit.length === 0) {
- return;
- }
-
- $multipleEdit.popover('destroy');
-
- $multipleEdit.popover({
- sanitize: false,
- container: "body",
- html: true,
- placement: "top",
- trigger: 'manual'
- });
- if ($multipleEdit && $multipleEdit.length > 0) {
- $multipleEdit.off('click.multiple').on('click.multiple', function (e) {
- $(".popover").hide();
- $(this).popover('show');
- });
- }
- },
-
- bindSpecValueUpload: function() {
- if ($('.spec-image-input').length === 0) {
- return;
- }
-
- $(document).off('change.specvalue', '.spec-image-input');
- $(document).on('change.specvalue', '.spec-image-input', function () {
- var specKey = parseInt($(this).data('spec-key'));
- var valueIndex = parseInt($(this).data('value-index'));
- var imageUrl = $(this).val();
-
- if (vm && vm.specList && vm.specList[specKey] && vm.specList[specKey].value[valueIndex]) {
- vm.$set(vm.specList[specKey].value[valueIndex], 'image', imageUrl);
- }
- });
-
- setTimeout(function() {
- Controller.api.bindUploadButtons();
- }, 200);
- },
-
- bindUploadButtons: function() {
- if ($('.spec-upload-btn').length === 0) {
- return;
- }
-
- setTimeout(function() {
- require(['upload'], function (Upload) {
- $('.spec-upload-btn:not([initialized])').each(function() {
- var $btn = $(this);
- var specKey = $btn.attr('data-spec-key');
- var valueIndex = $btn.attr('data-value-index');
-
- if (specKey !== undefined && valueIndex !== undefined) {
- $btn.attr('initialized', true);
-
- if ($btn && $btn.length > 0) {
- $btn.off('click.specUpload').on('click.specUpload', function() {
- var inputId = 'c-spec-image-' + specKey + '-' + valueIndex;
- var previewId = 'p-spec-image-' + specKey + '-' + valueIndex;
-
- var tempBtn = $('<button type="button" class="faupload" style="display:none;"></button>');
- tempBtn.attr('data-input-id', inputId);
- tempBtn.attr('data-preview-id', previewId);
- tempBtn.attr('data-mimetype', $btn.attr('data-mimetype'));
- tempBtn.attr('data-multiple', $btn.attr('data-multiple'));
- $('body').append(tempBtn);
-
- Upload.api.upload(tempBtn, function(data, ret) {
- var url = data.url || '';
-
- if (vm && vm.specList && vm.specList[specKey] && vm.specList[specKey].value[valueIndex]) {
- vm.$set(vm.specList[specKey].value[valueIndex], 'image', url);
- }
-
- $('#' + inputId).val(url);
-
- var $preview = $('#' + previewId);
- if (url) {
- var img = '<img src="' + Fast.api.cdnurl(url) + '" style="width:100%;height:100%;object-fit:cover;">';
- $preview.html(img);
- } else {
- $preview.empty();
- }
-
- tempBtn.remove();
- }, function(data, ret) {
- Toastr.error(ret.msg || '上传失败');
- tempBtn.remove();
- });
-
- tempBtn.click();
- });
- }
- }
- });
-
- $('.spec-choose-btn:not([initialized])').each(function() {
- var $btn = $(this);
- if (!$btn || $btn.length === 0) {
- return;
- }
-
- var specKey = $btn.attr('data-spec-key');
- var valueIndex = $btn.attr('data-value-index');
-
- if (specKey !== undefined && valueIndex !== undefined) {
- $btn.attr('initialized', true);
-
- if ($btn && $btn.length > 0) {
- $btn.off('click.specChoose').on('click.specChoose', function() {
- var inputId = 'c-spec-image-' + specKey + '-' + valueIndex;
- var previewId = 'p-spec-image-' + specKey + '-' + valueIndex;
-
- parent.Fast.api.open("general/attachment/select?element_id=" + inputId + "&multiple=false&mimetype=image/*", __('Choose'), {
- callback: function(data) {
- var url = data.url || '';
-
- if (vm && vm.specList && vm.specList[specKey] && vm.specList[specKey].value[valueIndex]) {
- vm.$set(vm.specList[specKey].value[valueIndex], 'image', url);
- }
-
- $('#' + inputId).val(url);
-
- var $preview = $('#' + previewId);
- if (url) {
- var img = '<img src="' + Fast.api.cdnurl(url) + '" style="width:100%;height:100%;object-fit:cover;">';
- $preview.html(img);
- } else {
- $preview.empty();
- }
- }
- });
- });
- }
- }
- });
- });
- }, 100);
- },
- content: function (value, row, index) {
- var width = this.width != undefined ? (this.width.match(/^\d+$/) ? this.width + "px" : this.width) : "350px";
- return "<div style='white-space: nowrap; text-align:left; text-overflow:ellipsis; overflow: hidden; max-width:" + width + ";' title='" + value + "' data-toggle='tooltip' data-placement='right'>" + value + "</div>";
- },
- }
- };
- return Controller;
- });
|