activity.js 127 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716
  1. define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function ($, undefined, Backend, Table, Form, Template) {
  2. var Controller = {
  3. index: function () {
  4. // 初始化表格参数
  5. Table.api.init({
  6. extend: {
  7. index_url: 'lottery/activity/index' + location.search,
  8. add_url: 'lottery/activity/add',
  9. edit_url: 'lottery/activity/edit',
  10. del_url: 'lottery/activity/del',
  11. multi_url: 'lottery/activity/multi',
  12. import_url: 'lottery/activity/import',
  13. table: 'shop_lottery_activity',
  14. }
  15. });
  16. var table = $("#table");
  17. // 初始化表格
  18. table.bootstrapTable({
  19. url: $.fn.bootstrapTable.defaults.extend.index_url,
  20. pk: 'id',
  21. sortName: 'id',
  22. fixedColumns: true,
  23. fixedRightNumber: 1,
  24. columns: [
  25. [
  26. {checkbox: true},
  27. {field: 'id', title: __('Id'), operate: false},
  28. {field: 'name', title: __('活动名称'), operate: 'LIKE'},
  29. {field: 'type', title: __('活动类型'), searchList: Controller.api.parseConfigJson("typeList"), formatter: Table.api.formatter.normal},
  30. {field: 'status', title: __('状态'), searchList: Controller.api.parseConfigJson("statusList"), formatter: Table.api.formatter.status},
  31. {field: 'lottery_type', title: __('开奖方式'), searchList: Controller.api.parseConfigJson("lotteryTypeList"), formatter: Table.api.formatter.normal},
  32. {field: 'start_time', title: __('开始时间'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
  33. {field: 'end_time', title: __('结束时间'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
  34. {field: 'total_people_count', title: __('参与人数'), operate: false},
  35. {field: 'total_draw_count', title: __('抽奖次数'), operate: false},
  36. {field: 'total_win_count', title: __('中奖次数'), operate: false},
  37. {field: 'createtime', title: __('Create time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
  38. {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate,
  39. buttons: [
  40. // {
  41. // name: 'prize',
  42. // title: __('奖品管理'),
  43. // classname: 'btn btn-xs btn-success btn-magic btn-ajax',
  44. // icon: 'fa fa-gift',
  45. // url: 'lottery/activity/prize',
  46. // callback: function (data) {
  47. // Layer.alert("接收到回传数据:" + JSON.stringify(data), {title: "回传数据"});
  48. // },
  49. // visible: function (row) {
  50. // //返回true时按钮显示,返回false隐藏
  51. // return true;
  52. // }
  53. // },
  54. // {
  55. // name: 'condition',
  56. // title: __('参与条件'),
  57. // classname: 'btn btn-xs btn-info btn-magic btn-ajax',
  58. // icon: 'fa fa-tasks',
  59. // url: 'lottery/activity/condition'
  60. // },
  61. // {
  62. // name: 'records',
  63. // title: __('抽奖记录'),
  64. // classname: 'btn btn-xs btn-warning btn-magic btn-ajax',
  65. // icon: 'fa fa-list',
  66. // url: 'lottery/activity/records'
  67. // },
  68. // {
  69. // name: 'statistics',
  70. // title: __('统计数据'),
  71. // classname: 'btn btn-xs btn-primary btn-magic btn-ajax',
  72. // icon: 'fa fa-bar-chart',
  73. // url: 'lottery/activity/statistics'
  74. // }
  75. ],
  76. formatter: Table.api.formatter.operate
  77. }
  78. ]
  79. ]
  80. });
  81. // 为表格绑定事件
  82. Table.api.bindevent(table);
  83. },
  84. add: function () {
  85. Controller.api.bindevent();
  86. Controller.api.initAddPage();
  87. },
  88. edit: function () {
  89. Controller.api.bindevent();
  90. Controller.api.initEditPage();
  91. },
  92. api: {
  93. // 解析Config中的JSON字符串的辅助函数
  94. parseConfigJson: function(configKey, defaultValue) {
  95. var configValue = Config[configKey] || defaultValue || {};
  96. // 如果是字符串,尝试解析JSON
  97. if (typeof configValue === 'string') {
  98. try {
  99. return JSON.parse(configValue);
  100. } catch (e) {
  101. return defaultValue || {};
  102. }
  103. }
  104. return configValue;
  105. },
  106. bindevent: function () {
  107. Form.api.bindevent($("form[role=form]"));
  108. },
  109. // 初始化添加页面
  110. initAddPage: function() {
  111. // 初始化基础UI组件
  112. Controller.api.initBasicUI();
  113. // 初始化奖品数据管理器
  114. Controller.api.prizesDataManager.init();
  115. // 初始化规则数据管理器
  116. Controller.api.rulesDataManager.init();
  117. // 初始化奖品管理
  118. Controller.api.initPrizeManagement();
  119. // 初始化任务规则管理
  120. Controller.api.initTaskRuleManagement();
  121. // 清空奖品列表DOM,确保没有任何默认奖品
  122. $('.prize-list').empty();
  123. // 添加页面:用户手动添加所有奖品,不创建任何默认项
  124. console.log('添加页面初始化完成,奖品列表已清空,等待用户手动添加奖品');
  125. // 确保DOM渲染完成后再计算奖品数量
  126. setTimeout(function() {
  127. console.log('页面初始化 - 开始更新奖品计数');
  128. Controller.api.updatePrizeCount();
  129. }, 50);
  130. // 初始化表单验证
  131. Controller.api.initFormValidation();
  132. },
  133. // 初始化基础UI组件
  134. initBasicUI: function() {
  135. // 活动名称字数统计
  136. $('#c-name').on('input', function(){
  137. var len = $(this).val().length;
  138. $('#activity-name-count').text(len + '/24');
  139. });
  140. },
  141. // 初始化任务规则管理
  142. initTaskRuleManagement: function() {
  143. // 任务类型切换(复选框)
  144. $(document).on('change', 'input[name="row[task_type][]"]', function() {
  145. Controller.api.handleTaskTypeChange();
  146. });
  147. // 商品规则切换
  148. $(document).on('change', 'input[name^="condition"][name$="[goods_rule]"]', function() {
  149. Controller.api.updateGoodsRuleDisplay($(this));
  150. // 更新规则数据管理器
  151. Controller.api.updateRulesDataFromDOM();
  152. });
  153. // 条件值输入框变化事件
  154. $(document).on('input change', 'input[name^="condition"][name$="[condition_value]"]', function() {
  155. // 更新规则数据管理器
  156. Controller.api.updateRulesDataFromDOM();
  157. });
  158. // 初始化任务类型状态
  159. Controller.api.initTaskTypeState();
  160. },
  161. // 处理任务类型变化
  162. handleTaskTypeChange: function() {
  163. var checkedTypes = $('input[name="row[task_type][]"]:checked');
  164. // 更新复选框样式
  165. $('input[name="row[task_type][]"]').each(function() {
  166. var label = $(this).closest('label');
  167. if ($(this).is(':checked')) {
  168. label.addClass('checked');
  169. } else {
  170. label.removeClass('checked');
  171. }
  172. });
  173. // 隐藏所有任务设置项
  174. $('.task-setting-item').hide();
  175. // 显示选中的任务设置项
  176. checkedTypes.each(function() {
  177. var type = parseInt($(this).val());
  178. $('#task-setting-' + type).show();
  179. });
  180. // 更新验证规则
  181. Controller.api.updateTaskValidation();
  182. // 更新规则数据管理器
  183. Controller.api.updateRulesDataFromDOM();
  184. },
  185. // 初始化任务类型状态
  186. initTaskTypeState: function() {
  187. var checkedTasks = $('input[name="row[task_type][]"]:checked');
  188. // 初始化复选框样式
  189. $('input[name="row[task_type][]"]').each(function() {
  190. var label = $(this).closest('label');
  191. if ($(this).is(':checked')) {
  192. label.addClass('checked');
  193. } else {
  194. label.removeClass('checked');
  195. }
  196. });
  197. // 初始化任务设置区域显示
  198. $('.task-setting-item').hide();
  199. checkedTasks.each(function() {
  200. var type = parseInt($(this).val());
  201. $('#task-setting-' + type).show();
  202. });
  203. },
  204. // 更新商品规则显示
  205. updateGoodsRuleDisplay: function($radio) {
  206. var container = $radio.closest('.task-setting-item');
  207. var rule = $radio.val();
  208. var helpText = container.find('.goods-description-box');
  209. if (rule == '1') {
  210. helpText.html('<span class="text-muted">指定商品下单后将自动触发抽奖活动且发放一次抽奖机会,若用户产生维权退款,已发放的奖励自动回收。</span>');
  211. } else {
  212. helpText.html('<span class="text-muted">除指定商品外的其他商品下单后将自动触发抽奖活动且发放一次抽奖机会。</span>');
  213. }
  214. },
  215. // 更新任务验证规则
  216. updateTaskValidation: function() {
  217. var checkedTypes = $('input[name="row[task_type][]"]:checked');
  218. // 移除旧的验证规则
  219. $('[name^="condition"]').removeAttr('data-rule');
  220. // 为选中的任务类型添加验证规则
  221. checkedTypes.each(function() {
  222. var type = parseInt($(this).val());
  223. switch(type) {
  224. case 1: // 购买指定商品
  225. // 商品选择验证会在选择商品时处理
  226. break;
  227. case 2: // 单笔订单消费满额
  228. case 3: // 单次充值满额
  229. case 4: // 活动期间累计消费满额
  230. $('[name="condition[' + type + '][condition_value]"]').attr('data-rule', 'required;number(0.01~999999.99)');
  231. break;
  232. }
  233. });
  234. },
  235. // 初始化表单验证
  236. initFormValidation: function() {
  237. // 添加自定义验证规则 - 验证奖品JSON数据
  238. if (typeof Nice !== 'undefined' && Nice.Validator) {
  239. Nice.Validator.addRule('prizesJson', function(val, el) {
  240. try {
  241. var prizes = JSON.parse(val || '[]');
  242. // 验证是否有奖品
  243. if (!prizes || prizes.length === 0) {
  244. return false;
  245. }
  246. var totalRate = 0;
  247. // 验证每个奖品的基本字段
  248. for (var i = 0; i < prizes.length; i++) {
  249. var prize = prizes[i];
  250. // 验证必填字段
  251. if (!prize.name || prize.name.trim() === '') {
  252. return false;
  253. }
  254. if (!prize.type) {
  255. return false;
  256. }
  257. // 验证奖品类型是否有效
  258. if (!prize.type || prize.type < 1 || prize.type > 8) {
  259. return false;
  260. }
  261. // 验证概率值
  262. if (typeof prize.rate !== 'number' || prize.rate < 0 || prize.rate > 100) {
  263. return false;
  264. }
  265. // 验证数量(未中奖项除外)
  266. if (prize.type !== 1 && (!prize.quantity || prize.quantity <= 0)) {
  267. return false;
  268. }
  269. // 统计总概率
  270. totalRate += parseFloat(prize.rate || 0);
  271. }
  272. // 验证总概率必须等于100%
  273. if (Math.abs(totalRate - 100) > 0.01) {
  274. return false;
  275. }
  276. return true;
  277. } catch (e) {
  278. return false;
  279. }
  280. }, '奖品数据不完整或格式错误,请检查:1.总概率必须等于100% 2.实物奖品数量必须大于0');
  281. // 添加自定义验证规则 - 验证规则JSON数据
  282. Nice.Validator.addRule('rulesJson', function(val, el) {
  283. try {
  284. var rules = JSON.parse(val || '{}');
  285. // 验证是否有规则
  286. if (!rules || Object.keys(rules).length === 0) {
  287. return false;
  288. }
  289. // 验证每个规则的基本字段
  290. for (var type in rules) {
  291. var rule = rules[type];
  292. if (!rule.type) {
  293. return false;
  294. }
  295. // 根据类型验证特定字段
  296. switch(parseInt(type)) {
  297. case 1: // 购买指定商品
  298. if (!rule.goods_ids || !Array.isArray(JSON.parse(rule.goods_ids || '[]'))) {
  299. return false;
  300. }
  301. break;
  302. case 2: // 单笔订单消费满额
  303. case 3: // 单次充值满额
  304. case 4: // 活动期间累计消费满额
  305. if (!rule.condition_value || parseFloat(rule.condition_value) <= 0) {
  306. return false;
  307. }
  308. break;
  309. }
  310. }
  311. return true;
  312. } catch (e) {
  313. return false;
  314. }
  315. }, '规则设置不完整或格式错误');
  316. // 添加自定义验证规则 - 验证条件JSON数据
  317. Nice.Validator.addRule('conditionsJson', function(val, el) {
  318. try {
  319. var conditions = JSON.parse(val || '{}');
  320. // 验证是否有条件
  321. if (!conditions || Object.keys(conditions).length === 0) {
  322. return false;
  323. }
  324. // 验证每个条件的基本字段
  325. for (var type in conditions) {
  326. var condition = conditions[type];
  327. if (!condition.type || condition.type != type) {
  328. return false;
  329. }
  330. // 根据类型验证特定字段
  331. switch(parseInt(type)) {
  332. case 1: // 购买指定商品
  333. var goodsIds = JSON.parse(condition.goods_ids || '[]');
  334. if (!goodsIds || goodsIds.length === 0) {
  335. return false;
  336. }
  337. if (!condition.goods_rule || (condition.goods_rule != 1 && condition.goods_rule != 2)) {
  338. return false;
  339. }
  340. break;
  341. case 2: // 单笔订单消费满额
  342. case 3: // 单次充值满额
  343. case 4: // 活动期间累计消费满额
  344. if (!condition.condition_value || parseFloat(condition.condition_value) <= 0) {
  345. return false;
  346. }
  347. break;
  348. }
  349. }
  350. return true;
  351. } catch (e) {
  352. return false;
  353. }
  354. }, '参与条件设置不完整或格式错误,请检查:1.购买指定商品需要选择商品 2.消费/充值条件需要输入有效金额');
  355. }
  356. // 监听表单提交事件
  357. $('form[role="form"]').on('submit', function(e) {
  358. console.log('表单提交开始');
  359. // 验证任务规则设置
  360. if (!Controller.api.validateTaskRules()) {
  361. e.preventDefault();
  362. return false;
  363. }
  364. // 收集并验证条件数据
  365. try {
  366. Controller.api.collectAndSaveRulesData();
  367. // 验证条件数据是否满足要求
  368. var conditionsData = Controller.api.rulesDataManager.getAllRules();
  369. if (!conditionsData || Object.keys(conditionsData).length === 0) {
  370. Toastr.error('请设置至少一种参与条件');
  371. e.preventDefault();
  372. return false;
  373. }
  374. console.log('条件数据验证通过:', conditionsData);
  375. } catch (error) {
  376. console.error('条件数据验证失败:', error);
  377. Toastr.error('参与条件设置有误:' + error.message);
  378. e.preventDefault();
  379. return false;
  380. }
  381. return true;
  382. });
  383. },
  384. // 初始化编辑页面
  385. initEditPage: function() {
  386. // 初始化基础UI组件
  387. Controller.api.initBasicUI();
  388. // 初始化奖品数据管理器
  389. Controller.api.prizesDataManager.init();
  390. // 初始化规则数据管理器
  391. Controller.api.rulesDataManager.init();
  392. // 初始化奖品管理
  393. Controller.api.initPrizeManagement();
  394. // 初始化任务规则管理
  395. Controller.api.initTaskRuleManagement();
  396. // 初始化已有的奖品数据
  397. Controller.api.initExistingPrizes();
  398. // 初始化已有的规则数据
  399. Controller.api.initExistingRules();
  400. // 初始化已有的条件数据
  401. Controller.api.initExistingConditions();
  402. // 确保所有概率输入框有正确的值
  403. setTimeout(function() {
  404. Controller.api.initDefaultPrizeRate();
  405. }, 100);
  406. // 初始化表单验证
  407. Controller.api.initFormValidation();
  408. console.log('编辑页面初始化完成');
  409. },
  410. // 同步DOM中现有的奖品数据到数据管理器
  411. syncExistingPrizesToDataManager: function() {
  412. console.log('开始同步DOM中现有的奖品数据到数据管理器');
  413. var existingPrizes = [];
  414. // 遍历DOM中现有的奖品项
  415. $('.prize-item').each(function(index) {
  416. var $item = $(this);
  417. var prizeData = Controller.api.extractPrizeDataFromDOM($item, index);
  418. if (prizeData) {
  419. existingPrizes.push(prizeData);
  420. console.log('提取到奖品数据:', prizeData);
  421. }
  422. });
  423. // 如果有奖品数据,同步到数据管理器
  424. if (existingPrizes.length > 0) {
  425. Controller.api.prizesDataManager.prizesData = existingPrizes;
  426. Controller.api.prizesDataManager.saveToHiddenField();
  427. console.log('已同步奖品数据到数据管理器,共', existingPrizes.length, '个奖品');
  428. } else {
  429. console.log('DOM中没有找到现有的奖品数据');
  430. }
  431. },
  432. // 从DOM中提取奖品数据
  433. extractPrizeDataFromDOM: function($item, index) {
  434. try {
  435. var isNoPrize = $item.attr('data-type') === 'no-prize';
  436. var prizeData = {
  437. id: $item.attr('data-prize-id') || ('prize_' + Date.now() + '_' + index),
  438. name: '',
  439. type: 1,
  440. image: '',
  441. quantity: 0,
  442. rate: 0
  443. };
  444. // 提取奖品名称
  445. if (isNoPrize) {
  446. // 系统默认未中奖项
  447. prizeData.name = $item.find('.prize-name-input').val() || $item.find('td:eq(1)').text().trim() || '谢谢参与';
  448. prizeData.type = parseInt($item.find('.prize-type-input').val() || 1);
  449. prizeData.image = $item.find('.prize-image-input').val() || '';
  450. // 如果隐藏域中没有图片,尝试从img标签获取(去掉CDN前缀)
  451. if (!prizeData.image) {
  452. var imgSrc = $item.find('.prize-image').attr('src') || '';
  453. if (imgSrc && typeof Fast !== 'undefined' && Fast.api && Fast.api.cdnurl) {
  454. // 去掉CDN前缀,获取相对路径
  455. prizeData.image = imgSrc.replace(/^https?:\/\/[^\/]+/, '');
  456. } else {
  457. prizeData.image = imgSrc;
  458. }
  459. }
  460. prizeData.quantity = 0; // 未中奖项数量固定为0
  461. prizeData.rate = parseFloat($item.find('.prize-rate').val() || $item.find('#no-prize-rate-input').val() || 50);
  462. } else {
  463. // 用户添加的奖品
  464. prizeData.name = $item.find('.prize-name').text().trim();
  465. prizeData.type = parseInt($item.find('.prize-type-input').val() || 2);
  466. prizeData.image = $item.find('.prize-image-input').val() || $item.find('.prize-image').attr('src') || '';
  467. prizeData.quantity = parseInt($item.find('.prize-quantity').val() || 1);
  468. prizeData.rate = parseFloat($item.find('.prize-rate').val() || 0);
  469. }
  470. // 根据奖品类型提取特定数据
  471. switch(prizeData.type) {
  472. case 3: // 积分
  473. prizeData.points = parseInt($item.find('.prize-points-input').val() || 0);
  474. break;
  475. case 4: // 余额
  476. prizeData.balance = parseFloat($item.find('.prize-balance-input').val() || 0);
  477. break;
  478. case 5: // 优惠券
  479. prizeData.coupon_id = $item.find('.prize-coupon-id-input').val();
  480. prizeData.couponId = prizeData.coupon_id;
  481. prizeData.coupon_name = $item.find('.prize-coupon-name').text() || '';
  482. break;
  483. case 6: // 红包
  484. prizeData.redpack_amount = parseFloat($item.find('.prize-redpack-input').val() || 0);
  485. break;
  486. case 7: // 兑换码
  487. prizeData.code_type = $item.find('.prize-code-type-input').val() || 'auto';
  488. prizeData.manual_codes = $item.find('.prize-manual-codes-input').val() || '';
  489. break;
  490. case 8: // 商城奖品
  491. prizeData.goods_id = $item.find('.prize-goods-id-input').val();
  492. prizeData.goodsId = prizeData.goods_id;
  493. prizeData.goods_name = $item.find('.prize-goods-name').text() || '';
  494. break;
  495. }
  496. console.log('提取奖品数据:', {
  497. index: index,
  498. isNoPrize: isNoPrize,
  499. extractedData: prizeData
  500. });
  501. return prizeData;
  502. } catch (e) {
  503. console.warn('提取奖品数据时出错:', e, $item);
  504. return null;
  505. }
  506. },
  507. // 初始化已有的奖品数据(重构版 - 使用数据管理器)
  508. initExistingPrizes: function() {
  509. // 直接从Config中获取奖品数据
  510. var existingPrizes = Controller.api.parseConfigJson('existingPrizes', []);
  511. // 检查是否有后台传递的奖品数据
  512. if (existingPrizes && existingPrizes.length > 0) {
  513. console.log('正在初始化已有奖品数据,共', existingPrizes.length, '个奖品');
  514. // 处理奖品数据,确保格式正确
  515. var processedPrizes = [];
  516. for (var i = 0; i < existingPrizes.length; i++) {
  517. var prize = existingPrizes[i];
  518. var processedPrize = {
  519. id: prize.id || ('prize_' + Date.now() + '_' + i),
  520. name: prize.name || '',
  521. type: parseInt(prize.type) || 1,
  522. image: prize.image || '',
  523. description: prize.description || '',
  524. quantity: parseInt(prize.quantity) || 0,
  525. rate: parseFloat(prize.rate) || 0,
  526. sort_order: parseInt(prize.sort_order) || i
  527. };
  528. // 根据奖品类型设置特定字段
  529. switch (processedPrize.type) {
  530. case 3: // 积分
  531. processedPrize.points = parseInt(prize.points) || 0;
  532. break;
  533. case 4: // 余额
  534. processedPrize.balance = parseFloat(prize.balance) || 0;
  535. break;
  536. case 5: // 优惠券
  537. processedPrize.coupon_id = prize.coupon_id || '';
  538. processedPrize.coupon_name = prize.coupon_name || '';
  539. break;
  540. case 6: // 红包
  541. processedPrize.redpack_amount = parseFloat(prize.redpack_amount) || 0;
  542. break;
  543. case 7: // 兑换码
  544. processedPrize.code_type = prize.code_type || 'auto';
  545. processedPrize.manual_codes = prize.manual_codes || '';
  546. break;
  547. case 8: // 商城奖品
  548. processedPrize.goods_id = prize.goods_id || '';
  549. processedPrize.goods_name = prize.goods_name || '';
  550. break;
  551. }
  552. processedPrizes.push(processedPrize);
  553. }
  554. // 将处理后的奖品数据加载到数据管理器
  555. Controller.api.prizesDataManager.prizesData = processedPrizes;
  556. Controller.api.prizesDataManager.saveToHiddenField();
  557. // 重新构建奖品列表
  558. Controller.api.rebuildPrizeList();
  559. // 更新奖品计数和概率统计
  560. Controller.api.updatePrizeCount();
  561. Controller.api.updateTotalRate();
  562. console.log('奖品数据初始化完成,数据管理器已同步,共', processedPrizes.length, '个奖品');
  563. } else {
  564. console.log('编辑页面没有找到已有的奖品数据');
  565. }
  566. },
  567. // 初始化已有的规则数据
  568. initExistingRules: function() {
  569. // 直接从Config中获取规则数据
  570. var existingRulesData = Controller.api.parseConfigJson('existingRulesData', {});
  571. // 检查是否有后台传递的规则数据
  572. if (existingRulesData && Object.keys(existingRulesData).length > 0) {
  573. console.log('正在初始化已有规则数据:', existingRulesData);
  574. // 将已有规则数据加载到数据管理器
  575. Controller.api.rulesDataManager.rulesData = existingRulesData;
  576. Controller.api.rulesDataManager.saveToHiddenField();
  577. // 根据规则数据设置任务类型选择状态
  578. Controller.api.renderTaskTypesFromRules(existingRulesData);
  579. console.log('规则数据初始化完成');
  580. } else {
  581. console.log('没有找到已有的规则数据');
  582. }
  583. },
  584. // 根据规则数据渲染任务类型选择
  585. renderTaskTypesFromRules: function(rulesData) {
  586. console.log('根据规则数据渲染任务类型选择:', rulesData);
  587. // 取消所有任务类型的选中状态
  588. $('input[name="row[task_type][]"]').prop('checked', false);
  589. // 隐藏所有任务设置项
  590. $('.task-setting-item').hide();
  591. // 遍历规则数据,设置对应的任务类型
  592. for (var type in rulesData) {
  593. var rule = rulesData[type];
  594. var taskType = parseInt(type);
  595. // 勾选对应的任务类型
  596. var checkbox = $('input[name="row[task_type][]"][value="' + taskType + '"]');
  597. if (checkbox.length > 0) {
  598. checkbox.prop('checked', true);
  599. // 显示对应的任务设置
  600. $('#task-setting-' + taskType).show();
  601. // 根据任务类型设置具体的数据
  602. Controller.api.renderTaskSettingData(taskType, rule);
  603. }
  604. }
  605. // 更新复选框样式
  606. Controller.api.handleTaskTypeChange();
  607. },
  608. // 渲染任务设置数据
  609. renderTaskSettingData: function(taskType, ruleData) {
  610. console.log('渲染任务设置数据:', taskType, ruleData);
  611. switch (parseInt(taskType)) {
  612. case 1: // 购买指定商品
  613. // 设置商品规则
  614. if (ruleData.goods_rule) {
  615. $('input[name="condition[' + taskType + '][goods_rule]"][value="' + ruleData.goods_rule + '"]').prop('checked', true);
  616. Controller.api.updateGoodsRuleDisplay($('input[name="condition[' + taskType + '][goods_rule]"]:checked'));
  617. }
  618. // 设置商品选择
  619. if (ruleData.goods_ids) {
  620. var goodsIds = JSON.parse(ruleData.goods_ids || '[]');
  621. if (goodsIds.length > 0) {
  622. // 更新隐藏字段
  623. $('#task-goods-ids-' + taskType).val(ruleData.goods_ids);
  624. // 调用商品信息API来渲染商品列表
  625. Controller.api.loadAndRenderTaskGoods(taskType, goodsIds);
  626. }
  627. }
  628. break;
  629. case 2: // 单笔订单消费满额
  630. case 3: // 单次充值满额
  631. case 4: // 活动期间累计消费满额
  632. // 设置条件值
  633. if (ruleData.condition_value) {
  634. var amountInput = $('input[name="condition[' + taskType + '][condition_value]"]');
  635. if (amountInput.length > 0) {
  636. amountInput.val(ruleData.condition_value);
  637. }
  638. }
  639. break;
  640. }
  641. },
  642. // 加载并渲染任务商品
  643. loadAndRenderTaskGoods: function(taskType, goodsIds) {
  644. if (!goodsIds || goodsIds.length === 0) return;
  645. console.log('加载任务商品数据:', taskType, goodsIds);
  646. // 调用商品信息API
  647. $.ajax({
  648. url: Fast.api.fixurl('shop/goods/getGoodsByIds'),
  649. type: 'POST',
  650. data: {
  651. ids: goodsIds.join(',')
  652. },
  653. dataType: 'json',
  654. success: function(ret) {
  655. if (ret.code === 1 && ret.data && ret.data.length > 0) {
  656. // 更新商品显示
  657. Controller.api.updateSelectedGoods(ret.data, {
  658. container: '#selected-goods',
  659. template: 'goods-list-template'
  660. });
  661. // 绑定删除事件
  662. Controller.api.bindTaskGoodsEvents();
  663. console.log('任务商品数据渲染完成:', ret.data);
  664. } else {
  665. console.warn('加载任务商品数据失败:', ret);
  666. // 显示友好的错误提示
  667. $('#selected-goods').html(
  668. '<div class="alert alert-warning" style="margin-top: 10px;">' +
  669. '<i class="fa fa-warning"></i> 部分商品数据加载失败,请重新选择商品' +
  670. '</div>'
  671. );
  672. }
  673. },
  674. error: function(xhr, status, error) {
  675. console.error('加载任务商品数据失败:', error);
  676. $('#selected-goods').html(
  677. '<div class="alert alert-danger" style="margin-top: 10px;">' +
  678. '<i class="fa fa-exclamation-circle"></i> 商品数据加载失败,请检查网络连接' +
  679. '</div>'
  680. );
  681. }
  682. });
  683. },
  684. // 初始化已有的条件数据(已合并到initExistingRules中)
  685. initExistingConditions: function() {
  686. console.log('条件数据初始化已合并到规则数据管理器中');
  687. },
  688. // 验证任务规则设置
  689. validateTaskRules: function() {
  690. var checkedTypes = $('input[name="row[task_type][]"]:checked');
  691. // 检查是否选择了任务类型
  692. if (checkedTypes.length === 0) {
  693. Toastr.error('请选择至少一种参与条件');
  694. return false;
  695. }
  696. // 验证每个选中的任务类型
  697. var isValid = true;
  698. checkedTypes.each(function() {
  699. var type = parseInt($(this).val());
  700. var container = $('#task-setting-' + type);
  701. switch(type) {
  702. case 1: // 购买指定商品
  703. var goodsContainer = container.find('#selected-goods');
  704. if (goodsContainer.find('.goods-list-item').length === 0) {
  705. Toastr.error('请选择参与的商品');
  706. isValid = false;
  707. return false;
  708. }
  709. break;
  710. case 2: // 单笔订单消费满额
  711. case 3: // 单次充值满额
  712. case 4: // 活动期间累计消费满额
  713. var amountInput = container.find('input[name*="[condition_value]"]');
  714. var amount = parseFloat(amountInput.val()) || 0;
  715. if (amount <= 0) {
  716. var typeName = type === 2 ? '订单消费' : (type === 3 ? '充值' : '累计消费');
  717. Toastr.error('请填写' + typeName + '金额');
  718. amountInput.focus();
  719. isValid = false;
  720. return false;
  721. }
  722. break;
  723. }
  724. });
  725. return isValid;
  726. },
  727. // 初始化奖品管理 - 重构版
  728. initPrizeManagement: function() {
  729. console.log('初始化奖品管理模块');
  730. // 初始化拖拽排序
  731. Controller.api.initPrizeSort();
  732. // 初始化概率计算
  733. Controller.api.updateTotalRate();
  734. // 绑定奖品相关事件
  735. Controller.api.bindPrizeEvents();
  736. // 初始化表单验证
  737. Controller.api.initPrizeFormValidation();
  738. console.log('奖品管理模块初始化完成');
  739. },
  740. // 添加奖品
  741. addPrize: function() {
  742. var totalCount = $('.prize-item').length; // 总奖品数
  743. var maxCount = 10; // 最多10个奖品
  744. if (totalCount >= maxCount) {
  745. Toastr.error('最多只能添加' + maxCount + '个奖品');
  746. return;
  747. }
  748. // 使用通用表单,添加模式
  749. Controller.api.openPrizeForm({
  750. mode: 'add',
  751. title: '添加奖品'
  752. });
  753. },
  754. // 初始化奖品表单验证
  755. initPrizeFormValidation: function() {
  756. console.log('初始化奖品表单验证');
  757. // FastAdmin使用Nice-validator,验证规则通过data-rule属性定义
  758. // 无需额外的自定义验证方法,表单验证会在Form.api.bindevent中自动处理
  759. console.log('FastAdmin表单验证已通过data-rule属性配置');
  760. },
  761. // 通用奖品表单 - 重构版
  762. openPrizeForm: function(options) {
  763. var defaults = {
  764. mode: 'add', // 'add' 或 'edit'
  765. title: '奖品表单',
  766. prizeItem: null,
  767. data: {
  768. name: '',
  769. image: '',
  770. type: '2', // 默认实物奖品
  771. quantity: '1',
  772. rate: '0.00'
  773. }
  774. };
  775. var config = $.extend({}, defaults, options);
  776. var isEditMode = config.mode === 'edit';
  777. // 获取奖品类型数据 - 显示所有可用类型
  778. var prizeTypeMap = Controller.api.parseConfigJson('prizeTypeList', {});
  779. var prizeTypes = [];
  780. for (var key in prizeTypeMap) {
  781. if (prizeTypeMap.hasOwnProperty(key)) {
  782. prizeTypes.push({
  783. value: parseInt(key),
  784. text: prizeTypeMap[key]
  785. });
  786. }
  787. }
  788. // 确定是否显示奖励设置区域
  789. var selectedType = parseInt(config.data.type) || (isEditMode ? 1 : 2);
  790. var rewardVisible = selectedType > 2;
  791. var rewardType = Controller.api.getRewardTypeByPrizeType(selectedType);
  792. // 生成表单HTML - 使用重构的模板
  793. var formHtml = Template('prize-form-template', {
  794. mode: config.mode,
  795. prizeTypes: prizeTypes,
  796. selectedType: selectedType
  797. });
  798. // 弹出表单
  799. Layer.open({
  800. type: 1,
  801. title: config.title,
  802. area: isEditMode ? ['80%', '80%'] : ['80%', '80%'],
  803. content: formHtml,
  804. btn: [isEditMode ? '保存' : '确定', '取消'],
  805. success: function(layero, index) {
  806. var $form = layero.find('.prize-form');
  807. // 初始化FastAdmin表单组件
  808. Form.api.bindevent($form);
  809. // 编辑模式需要设置表单数据
  810. if (isEditMode) {
  811. Controller.api.setFormData($form, config);
  812. } else {
  813. // 添加模式:设置默认奖品类型并触发相应逻辑
  814. var defaultType = selectedType || 2; // 默认实物奖品
  815. $form.find('input[name="row[prize_type]"][value="' + defaultType + '"]').prop('checked', true);
  816. // 设置默认图片
  817. Controller.api.setPrizeDefaultImage($form, defaultType);
  818. // 触发奖品类型改变事件
  819. $form.find('input[name="row[prize_type]"]').trigger('change');
  820. }
  821. // 设置正确的字数统计初始值
  822. var currentName = $form.find('input[name="prize_name"]').val();
  823. var currentLength = currentName ? currentName.length : 0;
  824. $form.find('#prize-name-count').text(currentLength + '/20');
  825. // 绑定自定义事件
  826. Controller.api.bindPrizeFormEvents($form, config);
  827. // 初始化字数统计
  828. Controller.api.initNameCounter($form);
  829. // 优化表单样式,使其更紧凑
  830. Controller.api.optimizePrizeFormStyle($form);
  831. console.log('奖品表单初始化完成');
  832. },
  833. yes: function(index, layero) {
  834. var $form = layero.find('.prize-form');
  835. var success = false;
  836. if (isEditMode) {
  837. success = Controller.api.updatePrize($form, config);
  838. } else {
  839. success = Controller.api.savePrize($form);
  840. }
  841. if (success) {
  842. Layer.close(index);
  843. }
  844. // 如果验证失败,success会是false,Layer不会关闭
  845. return success;
  846. }
  847. });
  848. },
  849. // 设置默认表单数据(添加模式)
  850. setDefaultFormData: function($form, config) {
  851. console.log('设置默认表单数据');
  852. // 设置默认奖品类型
  853. var defaultType = config.data.type || 2;
  854. $form.find('input[name="row[prize_type]"][value="' + defaultType + '"]').prop('checked', true);
  855. // 设置默认图片
  856. Controller.api.setPrizeDefaultImage($form, defaultType);
  857. // 初始化图片上传组件
  858. Controller.api.initImageUpload($form);
  859. // 触发奖品类型改变事件
  860. $form.find('input[name="row[prize_type]"]').trigger('change');
  861. console.log('默认表单数据设置完成');
  862. },
  863. // 初始化名称字符计数
  864. initNameCounter: function($form) {
  865. var $nameInput = $form.find('input[name="prize_name"]');
  866. var $counter = $form.find('#prize-name-count');
  867. // 初始化计数
  868. var currentLength = $nameInput.val().length;
  869. $counter.text(currentLength + '/20');
  870. // 绑定输入事件
  871. $nameInput.on('input', function() {
  872. var length = $(this).val().length;
  873. $counter.text(length + '/20');
  874. });
  875. },
  876. // 优化奖品表单样式,使其更紧凑
  877. optimizePrizeFormStyle: function($form) {
  878. // 减少表单组的间距
  879. $form.find('.form-group').css({
  880. 'margin-bottom': '15px'
  881. });
  882. // 减少奖励设置区域的间距
  883. $form.find('.reward-group').css({
  884. 'margin-bottom': '10px'
  885. });
  886. // 减少表单区块的间距
  887. $form.find('.form-section').css({
  888. 'margin-bottom': '20px'
  889. });
  890. // 减少帮助文本的上边距
  891. $form.find('.help-block').css({
  892. 'margin-top': '5px',
  893. 'margin-bottom': '0px'
  894. });
  895. // 优化警告框的间距
  896. $form.find('.alert').css({
  897. 'margin-top': '10px',
  898. 'margin-bottom': '10px',
  899. 'padding': '10px'
  900. });
  901. // 减少图片预览区域的间距
  902. $form.find('.plupload-preview').css({
  903. 'margin-top': '10px'
  904. });
  905. console.log('表单样式优化完成');
  906. },
  907. // 初始化图片上传组件
  908. initImageUpload: function($form) {
  909. console.log('初始化图片上传组件');
  910. // 使用FastAdmin的Form组件来重新初始化表单内的上传组件
  911. Form.api.bindevent($form);
  912. // 绑定图片输入框的变化事件,实现预览功能
  913. $form.find('input[name="prize_image"]').on('change', function() {
  914. var imageUrl = $(this).val();
  915. if (imageUrl) {
  916. // 生成预览HTML
  917. var previewHtml = '<li class="col-xs-3">' +
  918. '<img src="' + (typeof Fast !== 'undefined' && Fast.api ? Fast.api.cdnurl(imageUrl) : imageUrl) + '" ' +
  919. 'class="img-responsive img-thumbnail" style="max-width: 100px; max-height: 100px;">' +
  920. '</li>';
  921. // 更新预览区域
  922. $form.find('#p-prize-image').html(previewHtml);
  923. } else {
  924. // 清空预览
  925. $form.find('#p-prize-image').empty();
  926. }
  927. });
  928. console.log('图片上传组件初始化完成');
  929. },
  930. // 设置表单数据(编辑模式) - 重构版
  931. setFormData: function($form, config) {
  932. console.log('设置编辑表单数据', config.data);
  933. var data = config.data;
  934. // 设置基本字段
  935. $form.find('input[name="prize_name"]').val(data.name);
  936. $form.find('input[name="prize_image"]').val(data.image);
  937. $form.find('input[name="prize_quantity"]').val(data.quantity || '1');
  938. $form.find('input[name="prize_rate"]').val(parseFloat(data.rate || 0).toFixed(2));
  939. // 设置奖品类型
  940. $form.find('input[name="row[prize_type]"][value="' + data.type + '"]').prop('checked', true);
  941. // 根据奖品类型处理特殊字段
  942. switch(parseInt(data.type)) {
  943. case 3: // 积分
  944. if (data.points) {
  945. $form.find('input[name="reward_value_integral"]').val(data.points);
  946. }
  947. break;
  948. case 4: // 余额
  949. if (data.balance) {
  950. $form.find('input[name="reward_value_balance"]').val(data.balance);
  951. }
  952. break;
  953. case 5: // 优惠券
  954. // 支持两种字段名格式
  955. var couponId = data.couponId || data.coupon_id;
  956. if (couponId) {
  957. $form.find('input[name="coupon_id"]').val(couponId);
  958. Controller.api.loadCouponInfo($form, couponId);
  959. }
  960. break;
  961. case 6: // 红包
  962. if (data.redpack_amount) {
  963. $form.find('input[name="reward_value_redpack"]').val(data.redpack_amount);
  964. }
  965. break;
  966. case 7: // 兑换码
  967. if (data.code_type) {
  968. $form.find('input[name="code_type"][value="' + data.code_type + '"]').prop('checked', true);
  969. }
  970. if (data.manual_codes) {
  971. $form.find('textarea[name="manual_codes"]').val(data.manual_codes);
  972. }
  973. break;
  974. case 8: // 商城奖品
  975. // 支持两种字段名格式
  976. var goodsId = data.goodsId || data.goods_id;
  977. if (goodsId) {
  978. $form.find('input[name="goods_id"]').val(goodsId);
  979. Controller.api.loadGoodsInfo($form, goodsId);
  980. }
  981. break;
  982. }
  983. // 触发奖品类型改变事件以显示对应的奖励设置
  984. $form.find('input[name="row[prize_type]"]').trigger('change');
  985. // 初始化图片上传组件
  986. Controller.api.initImageUpload($form);
  987. // 触发图片预览更新
  988. if (data.image) {
  989. $form.find('input[name="prize_image"]').trigger('change');
  990. }
  991. console.log('编辑表单数据设置完成');
  992. },
  993. // 绑定奖品表单事件 - 重构版
  994. bindPrizeFormEvents: function($form, config) {
  995. console.log('绑定奖品表单事件');
  996. var isEditMode = config && config.mode === 'edit';
  997. // 奖品类型切换事件
  998. $form.find('input[name="row[prize_type]"]').off('change.prizeType').on('change.prizeType', function() {
  999. var type = parseInt($(this).val());
  1000. console.log('奖品类型切换至:', type);
  1001. // 显示/隐藏对应的奖励设置
  1002. Controller.api.toggleRewardSettings($form, type);
  1003. // 非编辑模式时设置默认图片
  1004. if (!isEditMode) {
  1005. Controller.api.setPrizeDefaultImage($form, type);
  1006. }
  1007. });
  1008. // 商品选择按钮
  1009. $form.find('#select-goods-btn').off('click.selectGoods').on('click.selectGoods', function() {
  1010. Controller.api.openGoodsSelector($form);
  1011. });
  1012. // 优惠券选择按钮
  1013. $form.find('#select-coupon-btn').off('click.selectCoupon').on('click.selectCoupon', function() {
  1014. Controller.api.openCouponSelector($form);
  1015. });
  1016. // 兑换码类型切换
  1017. $form.find('input[name="code_type"]').off('change.codeType').on('change.codeType', function() {
  1018. var codeType = $(this).val();
  1019. Controller.api.toggleManualCodesInput($form, codeType);
  1020. });
  1021. // 概率输入框格式化
  1022. $form.find('input[name="prize_rate"]').off('blur.rateFormat').on('blur.rateFormat', function() {
  1023. var value = parseFloat($(this).val()) || 0;
  1024. $(this).val(value.toFixed(2));
  1025. });
  1026. // 删除选中项事件
  1027. $form.find('.selected-display').off('click.removeItem').on('click.removeItem', '.remove-item', function() {
  1028. var $item = $(this).closest('.selected-item');
  1029. var $hiddenInput = $item.siblings('input[type="hidden"]');
  1030. $item.remove();
  1031. $hiddenInput.val('');
  1032. });
  1033. console.log('奖品表单事件绑定完成');
  1034. },
  1035. // 切换奖励设置显示
  1036. toggleRewardSettings: function($form, type) {
  1037. console.log('切换奖励设置显示:', type);
  1038. // 隐藏所有奖励设置
  1039. $form.find('.reward-group').removeClass('active').hide();
  1040. // 根据类型显示对应的设置
  1041. var rewardType = '';
  1042. switch(parseInt(type)) {
  1043. case 3:
  1044. rewardType = 'integral';
  1045. break;
  1046. case 4:
  1047. rewardType = 'balance';
  1048. break;
  1049. case 5:
  1050. rewardType = 'coupon';
  1051. break;
  1052. case 6:
  1053. rewardType = 'redpack';
  1054. break;
  1055. case 7:
  1056. rewardType = 'code';
  1057. break;
  1058. case 8:
  1059. rewardType = 'goods';
  1060. break;
  1061. }
  1062. if (rewardType) {
  1063. $form.find('.reward-group[data-type="' + rewardType + '"]').addClass('active').show();
  1064. }
  1065. },
  1066. // 切换手动兑换码输入
  1067. toggleManualCodesInput: function($form, codeType) {
  1068. var $container = $form.find('.manual-codes-container');
  1069. if (codeType === 'manual') {
  1070. $container.show();
  1071. } else {
  1072. $container.hide();
  1073. }
  1074. },
  1075. // 设置奖品默认图片
  1076. setPrizeDefaultImage: function(form, type) {
  1077. // 从后台配置获取默认图片
  1078. var defaultImages = Controller.api.parseConfigJson('prizeDefaultImages', {});
  1079. var defaultImage = defaultImages[type] || '/assets/img/default.png';
  1080. // 设置图片路径并触发change事件以更新预览
  1081. form.find('input[name="prize_image"]').val(defaultImage).trigger('change');
  1082. },
  1083. // 加载商品信息用于编辑模式回显
  1084. loadGoodsInfo: function(form, goodsId, callback) {
  1085. if (!goodsId) {
  1086. console.warn('loadGoodsInfo: 商品ID为空');
  1087. if (callback) callback();
  1088. return;
  1089. }
  1090. console.log('正在加载商品信息,ID:', goodsId);
  1091. // 通过Ajax获取商品信息
  1092. $.ajax({
  1093. url: Fast.api.fixurl('shop/goods/info'),
  1094. type: 'POST',
  1095. data: {id: goodsId},
  1096. dataType: 'json',
  1097. success: function(data) {
  1098. console.log('商品信息API响应:', data);
  1099. if (data.code === 1 && data.data) {
  1100. var goods = data.data; // 标准FastAdmin响应格式
  1101. console.log('成功获取商品信息:', goods);
  1102. // 处理商品数据
  1103. var processedGoods = {
  1104. id: goods.id,
  1105. name: goods.name || goods.title,
  1106. title: goods.title || goods.name,
  1107. image: goods.image ? Fast.api.cdnurl(goods.image) : '',
  1108. price: goods.price || goods.sellprice || '',
  1109. goods_sn: goods.goods_sn || '',
  1110. spec_type: goods.spec_type !== undefined ? goods.spec_type : null,
  1111. category: goods.category || null,
  1112. type_name: Controller.api.getGoodsTypeName(goods),
  1113. type_text: Controller.api.getGoodsTypeText(goods)
  1114. };
  1115. // 使用商品模板显示
  1116. var html = Template('prize-goods-template', processedGoods);
  1117. form.find('#selected-goods-display').html(html).show();
  1118. // 绑定删除事件
  1119. form.find('.remove-prize-goods').off('click').on('click', function() {
  1120. $(this).closest('.prize-goods-item').remove();
  1121. form.find('input[name="goods_id"]').val('');
  1122. });
  1123. console.log('商品信息已成功显示');
  1124. // 执行回调
  1125. if (callback) callback();
  1126. } else {
  1127. console.warn('商品信息API返回异常:', data);
  1128. // 显示友好的提示信息
  1129. var errorMsg = data.msg || '获取商品信息失败';
  1130. form.find('#selected-goods-display').html(
  1131. '<div class="alert alert-warning" style="margin-top: 10px;">' +
  1132. '<i class="fa fa-warning"></i> ' + errorMsg +
  1133. ' (商品ID: ' + goodsId + ')' +
  1134. '</div>'
  1135. ).show();
  1136. // 即使失败也执行回调
  1137. if (callback) callback();
  1138. }
  1139. },
  1140. error: function(xhr, status, error) {
  1141. console.error('加载商品信息失败 - 详细信息:');
  1142. console.error('- 商品ID:', goodsId);
  1143. console.error('- HTTP状态:', xhr.status);
  1144. console.error('- 错误信息:', error);
  1145. console.error('- 响应内容:', xhr.responseText);
  1146. // 显示友好的错误提示
  1147. var errorHtml = '<div class="alert alert-danger" style="margin-top: 10px;">' +
  1148. '<i class="fa fa-exclamation-circle"></i> 加载商品信息失败<br>' +
  1149. '<small>商品ID: ' + goodsId + '</small><br>' +
  1150. '<small>错误: ' + (xhr.status === 404 ? '商品不存在或已删除' :
  1151. xhr.status === 403 ? '无权限访问' :
  1152. '网络错误,请稍后重试') + '</small>' +
  1153. '</div>';
  1154. form.find('#selected-goods-display').html(errorHtml).show();
  1155. // 错误时也执行回调
  1156. if (callback) callback();
  1157. }
  1158. });
  1159. },
  1160. // 加载优惠券信息用于编辑模式回显
  1161. loadCouponInfo: function(form, couponId) {
  1162. if (!couponId) {
  1163. console.warn('loadCouponInfo: 优惠券ID为空');
  1164. return;
  1165. }
  1166. console.log('正在加载优惠券信息,ID:', couponId);
  1167. // 暂时显示一个简单的占位信息,提醒需要重新选择
  1168. var placeholderHtml = '<div class="alert alert-info" style="margin-top: 10px;">' +
  1169. '<i class="fa fa-info-circle"></i> 编辑模式下需要重新选择优惠券<br>' +
  1170. '<small>优惠券ID: ' + couponId + '</small>' +
  1171. '</div>';
  1172. form.find('#selected-coupon-display').html(placeholderHtml).show();
  1173. console.log('优惠券占位信息已显示,用户需要重新选择');
  1174. },
  1175. // 打开商品选择器
  1176. openGoodsSelector: function(form) {
  1177. Fast.api.open('shop/goods/select?selectMode=single', '选择商品', {
  1178. callback: function(data) {
  1179. if (data.length > 0) {
  1180. var goods = data[0]; // 取第一个选中的商品
  1181. // 处理商品数据,确保格式完整
  1182. var processedGoods = {
  1183. id: goods.id,
  1184. name: goods.name || goods.title,
  1185. title: goods.title || goods.name,
  1186. image: goods.image ? (typeof Fast !== 'undefined' && Fast.api ? Fast.api.cdnurl(goods.image) : goods.image) : '',
  1187. price: goods.price || goods.sellprice || '',
  1188. goods_sn: goods.goods_sn || '',
  1189. spec_type: goods.spec_type !== undefined ? goods.spec_type : null,
  1190. category: goods.category || null,
  1191. type_name: Controller.api.getGoodsTypeName(goods),
  1192. type_text: Controller.api.getGoodsTypeText(goods)
  1193. };
  1194. // 使用奖品商品模板
  1195. var html = Template('prize-goods-template', processedGoods);
  1196. form.find('#selected-goods-display').html(html).show();
  1197. // 设置隐藏域的值以触发验证
  1198. form.find('input[name="goods_id"]').val(goods.id);
  1199. // 设置商品图片为奖品图片并触发预览更新
  1200. if (goods.image) {
  1201. form.find('input[name="prize_image"]').val(goods.image).trigger('change');
  1202. }
  1203. // 绑定删除事件
  1204. form.find('.remove-prize-goods').off('click').on('click', function() {
  1205. $(this).closest('.prize-goods-item').remove();
  1206. form.find('input[name="goods_id"]').val(''); // 清空隐藏域
  1207. });
  1208. }
  1209. }
  1210. });
  1211. },
  1212. // 打开优惠券选择器
  1213. openCouponSelector: function(form) {
  1214. Fast.api.open('shop/coupon/select', '选择优惠券', {
  1215. callback: function(data) {
  1216. if (data.length > 0) {
  1217. var coupon = data[0]; // 取第一个选中的优惠券
  1218. // 直接使用模板ID
  1219. var html = Template('selected-coupon-template', {
  1220. name: coupon.name,
  1221. type_text: coupon.type_text || '优惠券',
  1222. id: coupon.id
  1223. });
  1224. form.find('#selected-coupon-display').html(html).show();
  1225. // 设置隐藏域的值以触发验证
  1226. form.find('input[name="coupon_id"]').val(coupon.id);
  1227. }
  1228. }
  1229. });
  1230. },
  1231. // 保存奖品 - 重构版(使用数据管理器)
  1232. savePrize: function($form) {
  1233. console.log('开始保存奖品');
  1234. // 收集表单数据
  1235. var prizeData = Controller.api.collectPrizeFormData($form);
  1236. if (!prizeData) {
  1237. return false; // 验证失败
  1238. }
  1239. // 使用数据管理器添加奖品
  1240. var prizeId = Controller.api.prizesDataManager.addPrize(prizeData);
  1241. // 添加奖品到显示列表(追加模式,不会覆盖现有奖品)
  1242. Controller.api.appendPrizeToList(prizeData);
  1243. // 更新相关统计
  1244. Controller.api.updatePrizeCount();
  1245. Controller.api.updateTotalRate();
  1246. console.log('奖品保存成功:', prizeData, '奖品ID:', prizeId);
  1247. return true;
  1248. },
  1249. // 收集奖品表单数据
  1250. collectPrizeFormData: function($form) {
  1251. console.log('收集奖品表单数据');
  1252. var name = $form.find('input[name="prize_name"]').val().trim();
  1253. var type = parseInt($form.find('input[name="row[prize_type]"]:checked').val());
  1254. var image = $form.find('input[name="prize_image"]').val();
  1255. var quantity = parseInt($form.find('input[name="prize_quantity"]').val()) || 1;
  1256. var rate = parseFloat($form.find('input[name="prize_rate"]').val()) || 0;
  1257. // 基础验证 - FastAdmin的data-rule已经处理了大部分验证
  1258. // 这里只做简单的空值检查,具体验证错误会由Nice-validator显示
  1259. if (!name) {
  1260. Toastr.error('请输入奖品名称');
  1261. return null;
  1262. }
  1263. if (!type) {
  1264. Toastr.error('请选择奖品类型');
  1265. return null;
  1266. }
  1267. if (!image) {
  1268. Toastr.error('请选择奖品图片');
  1269. return null;
  1270. }
  1271. // 构建奖品数据
  1272. var prizeData = {
  1273. name: name,
  1274. type: type,
  1275. image: image,
  1276. quantity: Controller.api.isNoPrizeType(type) ? 0 : quantity,
  1277. rate: rate
  1278. };
  1279. // 根据奖品类型收集特定数据
  1280. switch(type) {
  1281. case 3: // 积分
  1282. prizeData.points = parseInt($form.find('input[name="reward_value_integral"]').val()) || 0;
  1283. break;
  1284. case 4: // 余额
  1285. prizeData.balance = parseFloat($form.find('input[name="reward_value_balance"]').val()) || 0;
  1286. break;
  1287. case 5: // 优惠券
  1288. prizeData.coupon_id = $form.find('input[name="coupon_id"]').val();
  1289. prizeData.couponId = prizeData.coupon_id; // 为模板提供一致的字段名
  1290. // 从优惠券显示区域获取名称
  1291. prizeData.coupon_name = $form.find('#selected-coupon-display').text().trim() ||
  1292. $form.find('.selected-coupon-item span:first').text() || '';
  1293. break;
  1294. case 6: // 红包
  1295. prizeData.redpack_amount = parseFloat($form.find('input[name="reward_value_redpack"]').val()) || 0;
  1296. break;
  1297. case 7: // 兑换码
  1298. prizeData.code_type = $form.find('input[name="code_type"]:checked').val() || 'auto';
  1299. if (prizeData.code_type === 'manual') {
  1300. prizeData.manual_codes = $form.find('textarea[name="manual_codes"]').val() || '';
  1301. }
  1302. break;
  1303. case 8: // 商城奖品
  1304. prizeData.goods_id = $form.find('input[name="goods_id"]').val();
  1305. prizeData.goodsId = prizeData.goods_id; // 为模板提供一致的字段名
  1306. // 从商品显示区域获取名称
  1307. prizeData.goods_name = $form.find('#selected-goods-display .goods-name-display').text() ||
  1308. $form.find('#selected-goods-display .prize-goods-item .goods-name-display').text() ||
  1309. $form.find('.prize-goods-item .goods-name-display').text() || '';
  1310. break;
  1311. }
  1312. console.log('收集到的奖品数据:', prizeData);
  1313. return prizeData;
  1314. },
  1315. // 统一的奖品数据处理方法
  1316. processPrizeData: function(formData) {
  1317. var prizeData = {
  1318. name: formData.name || '',
  1319. type: parseInt(formData.type) || 1,
  1320. image: formData.image || '',
  1321. description: formData.description || '',
  1322. quantity: parseInt(formData.quantity) || 0,
  1323. rate: parseFloat(formData.rate) || 0
  1324. };
  1325. // 未中奖项的特殊处理
  1326. if (prizeData.type === 1) {
  1327. prizeData.quantity = 0;
  1328. prizeData.rate = prizeData.rate || 50; // 默认50%概率
  1329. }
  1330. // 根据奖品类型设置特定字段
  1331. switch (prizeData.type) {
  1332. case 3: // 积分
  1333. prizeData.points = parseInt(formData.points) || 0;
  1334. break;
  1335. case 4: // 余额
  1336. prizeData.balance = parseFloat(formData.balance) || 0;
  1337. break;
  1338. case 5: // 优惠券
  1339. prizeData.coupon_id = formData.coupon_id || '';
  1340. prizeData.coupon_name = formData.coupon_name || '';
  1341. break;
  1342. case 6: // 红包
  1343. prizeData.redpack_amount = parseFloat(formData.redpack_amount) || 0;
  1344. break;
  1345. case 7: // 兑换码
  1346. prizeData.code_type = formData.code_type || 'auto';
  1347. prizeData.manual_codes = formData.manual_codes || '';
  1348. break;
  1349. case 8: // 商城奖品
  1350. prizeData.goods_id = formData.goods_id || '';
  1351. prizeData.goods_name = formData.goods_name || '';
  1352. break;
  1353. }
  1354. return prizeData;
  1355. },
  1356. // 添加奖品到列表(编辑模式用)
  1357. addPrizeToList: function(prize, index) {
  1358. // 获取奖品类型信息
  1359. var typeName = Controller.api.getPrizeTypeName(prize.type);
  1360. var labelClass = Controller.api.getPrizeLabelClass(prize.type);
  1361. var rewardText = Controller.api.getPrizeRewardText(prize);
  1362. // 使用传入的索引
  1363. var prizeIndex = typeof index !== 'undefined' ? index : $('.prize-list .prize-item').length;
  1364. // 处理图片URL
  1365. var imageRelative = prize.image; // 相对路径,用于表单提交
  1366. var imageDisplay = prize.image ? (typeof Fast !== 'undefined' && Fast.api ? Fast.api.cdnurl(prize.image) : prize.image) : ''; // CDN URL,用于显示
  1367. // 生成唯一的奖品ID
  1368. var prizeId = prize.id || ('prize_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9));
  1369. // 使用外部模板 - 重构为仅展示用途
  1370. var prizeHtml = Template('prize-item-template', {
  1371. index: prizeIndex,
  1372. prizeId: prizeId,
  1373. prizeType: prize.type, // 直接使用奖品类型
  1374. name: prize.name, // 奖品名称
  1375. description: prize.description || '', // 奖品描述
  1376. type: prize.type, // 奖品类型
  1377. typeName: typeName, // 类型显示名称
  1378. labelClass: labelClass, // 标签样式类
  1379. rewardText: rewardText, // 奖励文本
  1380. imageUrl: imageDisplay, // CDN URL,用于显示
  1381. quantity: prize.quantity || 0, // 奖品数量
  1382. showQuantity: !Controller.api.isNoPrizeType(prize.type), // 是否显示数量
  1383. rate: parseFloat(prize.rate || 0).toFixed(2), // 确保概率值格式化为两位小数
  1384. isSystemPrize: false // 移除系统奖品概念
  1385. });
  1386. $('.prize-list').append(prizeHtml);
  1387. console.log('已添加奖品到列表:', prize.name, '类型:', typeName);
  1388. },
  1389. // 添加奖品到列表(新增模式用)- 重构版
  1390. appendPrizeToList: function(prize) {
  1391. // 获取奖品类型信息
  1392. var typeName = Controller.api.getPrizeTypeName(prize.type);
  1393. var labelClass = Controller.api.getPrizeLabelClass(prize.type);
  1394. var rewardText = Controller.api.getPrizeRewardText(prize);
  1395. // 获取当前奖品索引,确保与数据管理器中的索引一致
  1396. var prizeIndex = Controller.api.prizesDataManager.prizesData.length - 1;
  1397. // 处理图片URL
  1398. var imageDisplay = prize.image ? (typeof Fast !== 'undefined' && Fast.api ? Fast.api.cdnurl(prize.image) : prize.image) : '';
  1399. // 生成唯一的奖品ID
  1400. var prizeId = prize.id || ('prize_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9));
  1401. // 使用外部模板 - 重构版(仅用于展示)
  1402. var prizeHtml = Template('prize-item-template', {
  1403. index: prizeIndex,
  1404. prizeId: prizeId,
  1405. prizeType: prize.type, // 奖品类型
  1406. name: prize.name, // 奖品名称
  1407. description: '', // 奖品描述(新增模式通常为空)
  1408. type: prize.type, // 奖品类型
  1409. typeName: typeName, // 类型显示名称
  1410. labelClass: labelClass, // 标签样式类
  1411. rewardText: rewardText, // 奖励文本
  1412. imageUrl: imageDisplay, // CDN URL,用于显示
  1413. quantity: prize.quantity || 0, // 奖品数量
  1414. showQuantity: !Controller.api.isNoPrizeType(prize.type), // 是否显示数量
  1415. rate: parseFloat(prize.rate || 0).toFixed(2), // 确保概率值格式化为两位小数
  1416. isSystemPrize: false // 移除系统奖品概念
  1417. });
  1418. $('.prize-list').append(prizeHtml);
  1419. // 重新初始化拖拽排序
  1420. Controller.api.initPrizeSort();
  1421. console.log('奖品已追加到列表,索引:', prizeIndex, '数据:', prize);
  1422. },
  1423. // 初始化默认奖品概率值
  1424. initDefaultPrizeRate: function() {
  1425. console.log('=== 开始初始化奖品概率值 ===');
  1426. // 确保所有奖品概率输入框都有默认值
  1427. $('.prize-rate').each(function() {
  1428. var $this = $(this);
  1429. var currentValue = $this.val();
  1430. if (!currentValue || currentValue === '') {
  1431. $this.val('0.00'); // 所有奖品默认概率为0
  1432. }
  1433. });
  1434. // 初始化后更新总概率显示
  1435. Controller.api.updateTotalRate();
  1436. console.log('=== 奖品概率值初始化完成 ===');
  1437. },
  1438. // 绑定奖品相关事件(使用事件委托)
  1439. bindPrizeEvents: function() {
  1440. // 使用事件委托,避免重复绑定
  1441. $(document).off('input.prizeManagement change.prizeManagement click.prizeManagement');
  1442. // 概率输入框事件
  1443. $(document).on('input.prizeManagement', '.prize-rate', function() {
  1444. var $item = $(this).closest('.prize-item');
  1445. var index = parseInt($item.attr('data-index'));
  1446. var rate = parseFloat($(this).val()) || 0;
  1447. // 更新数据管理器中的数据
  1448. Controller.api.prizesDataManager.updatePrizeRate(index, rate);
  1449. // 实时更新总概率
  1450. Controller.api.updateTotalRate();
  1451. });
  1452. $(document).on('change.prizeManagement', '.prize-rate', function() {
  1453. // 失去焦点时格式化为两位小数
  1454. var value = parseFloat($(this).val()) || 0;
  1455. $(this).val(value.toFixed(2));
  1456. Controller.api.updateTotalRate();
  1457. });
  1458. // 奖品数量输入框事件
  1459. $(document).on('input.prizeManagement change.prizeManagement', '.prize-quantity', function() {
  1460. var $item = $(this).closest('.prize-item');
  1461. var index = parseInt($item.attr('data-index'));
  1462. var quantity = parseInt($(this).val()) || 0;
  1463. // 更新数据管理器中的数据
  1464. Controller.api.prizesDataManager.updatePrizeQuantity(index, quantity);
  1465. });
  1466. // 删除奖品按钮事件
  1467. $(document).on('click.prizeManagement', '.delete-prize', function() {
  1468. var $item = $(this).closest('.prize-item');
  1469. var index = parseInt($item.attr('data-index'));
  1470. // 直接删除奖品,不再有系统默认项的限制
  1471. Controller.api.deletePrizeByIndex(index);
  1472. });
  1473. // 编辑奖品按钮事件
  1474. $(document).on('click.prizeManagement', '.edit-prize', function() {
  1475. var $item = $(this).closest('.prize-item');
  1476. var index = parseInt($item.attr('data-index'));
  1477. Controller.api.editPrizeByIndex(index);
  1478. });
  1479. },
  1480. // 通过索引删除奖品
  1481. deletePrizeByIndex: function(index) {
  1482. // 从数据管理器中删除
  1483. if (Controller.api.prizesDataManager.deletePrize(index)) {
  1484. // 重新构建列表
  1485. Controller.api.rebuildPrizeList();
  1486. // 更新统计
  1487. Controller.api.updatePrizeCount();
  1488. Controller.api.updateTotalRate();
  1489. console.log('奖品已删除,索引:', index);
  1490. }
  1491. },
  1492. // 通过索引编辑奖品
  1493. editPrizeByIndex: function(index) {
  1494. var prizeData = Controller.api.prizesDataManager.getPrize(index);
  1495. if (!prizeData) {
  1496. Toastr.error('奖品数据不存在');
  1497. return;
  1498. }
  1499. // 使用通用表单,编辑模式
  1500. Controller.api.openPrizeForm({
  1501. mode: 'edit',
  1502. title: '编辑奖品',
  1503. prizeIndex: index,
  1504. data: prizeData
  1505. });
  1506. },
  1507. // 重新构建奖品列表
  1508. rebuildPrizeList: function() {
  1509. // 清空现有列表
  1510. $('.prize-list').empty();
  1511. // 获取所有奖品数据
  1512. var allPrizes = Controller.api.prizesDataManager.getAllPrizes();
  1513. // 重新渲染每个奖品
  1514. for (var i = 0; i < allPrizes.length; i++) {
  1515. Controller.api.addPrizeToList(allPrizes[i], i);
  1516. }
  1517. // 重新初始化拖拽排序
  1518. Controller.api.initPrizeSort();
  1519. console.log('奖品列表已重新构建,共', allPrizes.length, '个奖品');
  1520. },
  1521. // 奖品数据管理 - 使用单一JSON隐藏域
  1522. prizesDataManager: {
  1523. // 奖品数据数组
  1524. prizesData: [],
  1525. // 初始化奖品数据管理器
  1526. init: function() {
  1527. // 创建奖品数据隐藏域
  1528. if ($('#prizes-data-json').length === 0) {
  1529. $('<input>').attr({
  1530. type: 'hidden',
  1531. id: 'prizes-data-json',
  1532. name: 'prizes_json',
  1533. value: '[]',
  1534. 'data-rule': 'required;prizesJson',
  1535. 'data-msg-required': '请添加至少一个奖品',
  1536. 'data-msg-prizesJson': '奖品数据格式不正确'
  1537. }).appendTo('form[role="form"]');
  1538. }
  1539. // 初始化数据
  1540. this.loadFromHiddenField();
  1541. },
  1542. // 验证奖品数据
  1543. validatePrizeData: function(prizeData) {
  1544. if (!prizeData.name || prizeData.name.trim() === '') {
  1545. throw new Error('奖品名称不能为空');
  1546. }
  1547. if (!prizeData.type || !Controller.api.getAvailablePrizeTypes()[prizeData.type]) {
  1548. throw new Error('请选择正确的奖品类型');
  1549. }
  1550. if (prizeData.type != 1 && (!prizeData.quantity || prizeData.quantity <= 0)) {
  1551. throw new Error('奖品数量必须大于0');
  1552. }
  1553. if (prizeData.rate < 0 || prizeData.rate > 100) {
  1554. throw new Error('中奖概率必须在0-100之间');
  1555. }
  1556. return true;
  1557. },
  1558. // 从隐藏域加载数据
  1559. loadFromHiddenField: function() {
  1560. var jsonData = $('#prizes-data-json').val();
  1561. try {
  1562. this.prizesData = JSON.parse(jsonData || '[]');
  1563. } catch (e) {
  1564. this.prizesData = [];
  1565. console.warn('奖品数据格式错误,已重置为空数组');
  1566. }
  1567. },
  1568. // 保存数据到隐藏域
  1569. saveToHiddenField: function() {
  1570. var jsonData = JSON.stringify(this.prizesData);
  1571. $('#prizes-data-json').val(jsonData);
  1572. console.log('奖品数据已保存到隐藏域:', this.prizesData);
  1573. },
  1574. // 添加奖品
  1575. addPrize: function(prizeData) {
  1576. // 验证奖品数据
  1577. this.validatePrizeData(prizeData);
  1578. // 生成唯一ID
  1579. prizeData.id = 'prize_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
  1580. // 设置默认值
  1581. prizeData.description = prizeData.description || '';
  1582. prizeData.sort_order = this.prizesData.length;
  1583. this.prizesData.push(prizeData);
  1584. this.saveToHiddenField();
  1585. return prizeData.id;
  1586. },
  1587. // 更新奖品
  1588. updatePrize: function(index, prizeData) {
  1589. if (index >= 0 && index < this.prizesData.length) {
  1590. // 验证奖品数据
  1591. this.validatePrizeData(prizeData);
  1592. // 保持原有ID和排序
  1593. prizeData.id = this.prizesData[index].id;
  1594. prizeData.sort_order = this.prizesData[index].sort_order;
  1595. // 设置默认值
  1596. prizeData.description = prizeData.description || '';
  1597. this.prizesData[index] = prizeData;
  1598. this.saveToHiddenField();
  1599. return true;
  1600. }
  1601. return false;
  1602. },
  1603. // 删除奖品
  1604. deletePrize: function(index) {
  1605. if (index >= 0 && index < this.prizesData.length) {
  1606. this.prizesData.splice(index, 1);
  1607. this.saveToHiddenField();
  1608. return true;
  1609. }
  1610. return false;
  1611. },
  1612. // 获取奖品数据
  1613. getPrize: function(index) {
  1614. if (index >= 0 && index < this.prizesData.length) {
  1615. return this.prizesData[index];
  1616. }
  1617. return null;
  1618. },
  1619. // 获取所有奖品数据
  1620. getAllPrizes: function() {
  1621. return this.prizesData;
  1622. },
  1623. // 更新奖品数量
  1624. updatePrizeQuantity: function(index, quantity) {
  1625. if (index >= 0 && index < this.prizesData.length) {
  1626. this.prizesData[index].quantity = parseInt(quantity) || 0;
  1627. this.saveToHiddenField();
  1628. return true;
  1629. }
  1630. return false;
  1631. },
  1632. // 更新奖品概率
  1633. updatePrizeRate: function(index, rate) {
  1634. if (index >= 0 && index < this.prizesData.length) {
  1635. this.prizesData[index].rate = parseFloat(rate) || 0;
  1636. this.saveToHiddenField();
  1637. return true;
  1638. }
  1639. return false;
  1640. },
  1641. // 重新排序奖品
  1642. reorderPrizes: function(newOrder) {
  1643. var reorderedData = [];
  1644. for (var i = 0; i < newOrder.length; i++) {
  1645. var oldIndex = newOrder[i];
  1646. if (oldIndex >= 0 && oldIndex < this.prizesData.length) {
  1647. reorderedData.push(this.prizesData[oldIndex]);
  1648. }
  1649. }
  1650. this.prizesData = reorderedData;
  1651. this.saveToHiddenField();
  1652. }
  1653. },
  1654. // 实时更新奖品隐藏域数据(重构版 - 使用奖品数据管理器)
  1655. updatePrizeHiddenFields: function() {
  1656. // 使用奖品数据管理器保存数据
  1657. Controller.api.prizesDataManager.saveToHiddenField();
  1658. },
  1659. // 初始化奖品拖拽排序 - 重构版
  1660. initPrizeSort: function() {
  1661. require(['../libs/Sortable/Sortable'], function(Sortable) {
  1662. var prizeListEl = document.querySelector('.prize-list');
  1663. if (prizeListEl) {
  1664. // 如果已经初始化过,先销毁
  1665. if (prizeListEl.sortableInstance) {
  1666. prizeListEl.sortableInstance.destroy();
  1667. }
  1668. // 创建新的 Sortable 实例
  1669. prizeListEl.sortableInstance = new Sortable(prizeListEl, {
  1670. handle: '.prize-sort-handle',
  1671. animation: 150,
  1672. onUpdate: function(evt) {
  1673. // 拖拽排序后重新排列数据管理器中的数据
  1674. Controller.api.updatePrizeOrderFromDOM();
  1675. }
  1676. });
  1677. }
  1678. });
  1679. },
  1680. // 从DOM更新奖品顺序到数据管理器
  1681. updatePrizeOrderFromDOM: function() {
  1682. var newOrder = [];
  1683. $('.prize-list .prize-item').each(function(index) {
  1684. var oldIndex = parseInt($(this).attr('data-index'));
  1685. if (!isNaN(oldIndex)) {
  1686. newOrder.push(oldIndex);
  1687. }
  1688. });
  1689. // 重新排序数据管理器中的数据
  1690. Controller.api.prizesDataManager.reorderPrizes(newOrder);
  1691. // 重新构建列表以更新索引
  1692. Controller.api.rebuildPrizeList();
  1693. console.log('奖品拖拽排序完成,新顺序:', newOrder);
  1694. },
  1695. // 获取奖品类型名称
  1696. getPrizeTypeName: function(type) {
  1697. var prizeTypeMap = Controller.api.parseConfigJson('prizeTypeList', {});
  1698. return prizeTypeMap[type] || '未知';
  1699. },
  1700. // 获取可用的奖品类型(显示所有奖品类型)
  1701. getAvailablePrizeTypes: function() {
  1702. // 返回所有奖品类型,不再限制
  1703. return Controller.api.parseConfigJson('prizeTypeList', {});
  1704. },
  1705. // 获取奖品标签样式类
  1706. getPrizeLabelClass: function(type) {
  1707. var classMap = {
  1708. 1: 'default', // 未中奖 - 灰色
  1709. 2: 'primary', // 实物奖品 - 蓝色
  1710. 3: 'warning', // 积分 - 橙色
  1711. 4: 'info', // 余额 - 浅蓝色
  1712. 5: 'success', // 优惠券 - 绿色
  1713. 6: 'danger', // 红包 - 红色
  1714. 7: 'default', // 兑换码 - 灰色
  1715. 8: 'primary' // 商城奖品 - 蓝色
  1716. };
  1717. return classMap[type] || 'default';
  1718. },
  1719. // 获取奖品奖励文本
  1720. getPrizeRewardText: function(prize) {
  1721. if (!prize.type || prize.type == 1) {
  1722. return ''; // 未中奖不显示奖励文本
  1723. }
  1724. switch(parseInt(prize.type)) {
  1725. case 2: // 实物奖品
  1726. return '';
  1727. case 3: // 积分
  1728. return prize.points ? (prize.points + ' 积分') : '';
  1729. case 4: // 余额
  1730. return prize.balance ? (prize.balance + ' 余额') : '';
  1731. case 5: // 优惠券
  1732. return prize.coupon_name ? prize.coupon_name : '';
  1733. case 6: // 红包
  1734. return prize.redpack_amount ? (prize.redpack_amount + ' 元红包') : '';
  1735. case 7: // 兑换码
  1736. return prize.code_type == 'manual' ? '手动兑换码' : '自动兑换码';
  1737. case 8: // 商城奖品
  1738. return prize.goods_name ? prize.goods_name : '';
  1739. default:
  1740. return '';
  1741. }
  1742. },
  1743. // 从表单获取奖品奖励文本
  1744. getPrizeRewardTextFromForm: function(form, type) {
  1745. if (!type || type == 1) {
  1746. return ''; // 未中奖不显示奖励文本
  1747. }
  1748. switch(parseInt(type)) {
  1749. case 2: // 实物奖品
  1750. return '';
  1751. case 3: // 积分
  1752. var points = form.find('input[name="reward_value_integral"]').val();
  1753. return points ? (points + ' 积分') : '';
  1754. case 4: // 余额
  1755. var balance = form.find('input[name="reward_value_balance"]').val();
  1756. return balance ? (balance + ' 余额') : '';
  1757. case 5: // 优惠券
  1758. // 从优惠券显示区域获取名称
  1759. var couponName = form.find('#selected-coupon-display').text().trim() ||
  1760. form.find('.selected-coupon-item span:first').text();
  1761. return couponName || '';
  1762. case 6: // 红包
  1763. var amount = form.find('input[name="reward_value_redpack"]').val();
  1764. return amount ? (amount + ' 元红包') : '';
  1765. case 7: // 兑换码
  1766. var codeType = form.find('input[name="code_type"]:checked').val();
  1767. return codeType == 'manual' ? '手动兑换码' : '自动兑换码';
  1768. case 8: // 商城奖品
  1769. // 从商品显示区域获取名称
  1770. return form.find('#selected-goods-display .goods-name-display').text() ||
  1771. form.find('#selected-goods-display .prize-goods-item .goods-name-display').text() ||
  1772. form.find('.prize-goods-item .goods-name-display').text() || '';
  1773. default:
  1774. return '';
  1775. }
  1776. },
  1777. // 根据奖品类型获取奖励类型
  1778. getRewardTypeByPrizeType: function(prizeType) {
  1779. switch(parseInt(prizeType)) {
  1780. case 3: return 'integral'; // 积分
  1781. case 4: return 'balance'; // 余额
  1782. case 5: return 'coupon'; // 优惠券
  1783. case 6: return 'redbag'; // 红包
  1784. case 7: return 'code'; // 兑换码
  1785. case 8: return 'goods'; // 商城奖品
  1786. default: return 'none';
  1787. }
  1788. },
  1789. // 判断是否为未中奖类型
  1790. isNoPrizeType: function(type, element) {
  1791. var typeValue = parseInt(type);
  1792. var isNoPrizeByType = typeValue === 1;
  1793. var isNoPrizeByAttr = element && $(element).attr('data-type') === 'no-prize';
  1794. return isNoPrizeByType || isNoPrizeByAttr;
  1795. },
  1796. // 更新奖品数量统计
  1797. updatePrizeCount: function() {
  1798. // 从数据管理器获取准确的奖品数量
  1799. var totalCount = Controller.api.prizesDataManager.prizesData.length;
  1800. console.log('updatePrizeCount - 总奖品数:', totalCount);
  1801. // 显示总奖品数
  1802. $('#prize-count').text(totalCount);
  1803. // 更新添加奖品按钮状态
  1804. Controller.api.updateAddPrizeButtonState(totalCount);
  1805. },
  1806. // 更新添加奖品按钮状态
  1807. updateAddPrizeButtonState: function(totalCount) {
  1808. var maxCount = 10; // 最大奖品数量
  1809. var addButton = $('#add-prize-btn');
  1810. var remainingCount = maxCount - totalCount;
  1811. console.log('updateAddPrizeButtonState - 奖品数:', totalCount, '最大允许:', maxCount, '剩余:', remainingCount);
  1812. if (totalCount >= maxCount) {
  1813. // 达到最大数量,禁用按钮
  1814. addButton.prop('disabled', true).addClass('disabled');
  1815. addButton.find('.btn-text').text('已达最大数量');
  1816. // 更新按钮计数显示
  1817. addButton.html('<i class="fa fa-plus"></i> 添加奖品 (10/10)');
  1818. $('#prize-count-tip').text('已添加最大数量的奖品(' + maxCount + '个)').removeClass('text-muted').addClass('text-danger');
  1819. console.log('按钮已禁用 - 达到最大数量');
  1820. } else {
  1821. // 未达到最大数量,启用按钮
  1822. addButton.prop('disabled', false).removeClass('disabled');
  1823. // 更新按钮计数显示
  1824. addButton.html('<i class="fa fa-plus"></i> 添加奖品 (' + totalCount + '/10)');
  1825. if (remainingCount <= 2) {
  1826. $('#prize-count-tip').text('还可添加 ' + remainingCount + ' 个奖品').removeClass('text-muted text-danger').addClass('text-warning');
  1827. } else {
  1828. $('#prize-count-tip').text('还可添加 ' + remainingCount + ' 个奖品').removeClass('text-warning text-danger').addClass('text-muted');
  1829. }
  1830. console.log('按钮已启用 - 剩余数量:', remainingCount);
  1831. }
  1832. },
  1833. // 计算总概率
  1834. calculateTotalRate: function() {
  1835. var total = 0;
  1836. $('.prize-rate').each(function() {
  1837. total += parseFloat($(this).val()) || 0;
  1838. });
  1839. // 保留两位小数
  1840. return Math.round(total * 100) / 100;
  1841. },
  1842. // 更新总概率显示
  1843. updateTotalRate: function() {
  1844. var total = Controller.api.calculateTotalRate();
  1845. // 格式化显示为两位小数
  1846. $('#total-rate').text(total.toFixed(2) + '%');
  1847. // 根据概率设置颜色
  1848. if (total > 100) {
  1849. $('#total-rate').removeClass('text-success text-warning').addClass('text-danger');
  1850. } else if (total === 100) {
  1851. $('#total-rate').removeClass('text-danger text-warning').addClass('text-success');
  1852. } else {
  1853. $('#total-rate').removeClass('text-danger text-success').addClass('text-warning');
  1854. }
  1855. },
  1856. // 编辑奖品(统一处理所有类型) - 已弃用,请使用 editPrizeByIndex
  1857. editPrize: function(prizeItem) {
  1858. // 获取奖品索引
  1859. var prizeIndex = parseInt(prizeItem.attr('data-index'));
  1860. if (isNaN(prizeIndex)) {
  1861. Toastr.error('无法获取奖品索引');
  1862. return;
  1863. }
  1864. // 调用新的编辑方法
  1865. Controller.api.editPrizeByIndex(prizeIndex);
  1866. },
  1867. // 更新奖品信息(重构版 - 使用数据管理器)
  1868. updatePrize: function($form, config) {
  1869. console.log('updatePrize 调用参数:', {form: $form, config: config});
  1870. // 验证config参数
  1871. if (!config || typeof config !== 'object') {
  1872. console.error('updatePrize: config参数无效:', config);
  1873. Toastr.error('配置参数错误');
  1874. return false;
  1875. }
  1876. // 验证prizeIndex
  1877. if (typeof config.prizeIndex === 'undefined' || config.prizeIndex === null) {
  1878. console.error('updatePrize: prizeIndex参数缺失:', config);
  1879. Toastr.error('奖品索引参数缺失');
  1880. return false;
  1881. }
  1882. // 收集表单数据
  1883. var prizeData = Controller.api.collectPrizeFormData($form);
  1884. if (!prizeData) {
  1885. return false; // 验证失败
  1886. }
  1887. var prizeIndex = parseInt(config.prizeIndex);
  1888. if (isNaN(prizeIndex) || prizeIndex < 0) {
  1889. console.error('updatePrize: prizeIndex无效:', config.prizeIndex);
  1890. Toastr.error('奖品索引无效');
  1891. return false;
  1892. }
  1893. // 使用数据管理器更新奖品
  1894. if (Controller.api.prizesDataManager.updatePrize(prizeIndex, prizeData)) {
  1895. // 重新构建列表以反映更新
  1896. Controller.api.rebuildPrizeList();
  1897. // 更新统计
  1898. Controller.api.updatePrizeCount();
  1899. Controller.api.updateTotalRate();
  1900. console.log('奖品更新成功:', prizeData, '索引:', prizeIndex);
  1901. return true;
  1902. } else {
  1903. console.error('updatePrize: 数据管理器更新失败');
  1904. Toastr.error('更新奖品失败');
  1905. return false;
  1906. }
  1907. },
  1908. // 在表单提交前收集条件数据 - 已弃用,改为使用规则数据管理器
  1909. collectConditionData: function() {
  1910. // 这个方法已经不再使用,改为使用规则数据管理器
  1911. console.warn('collectConditionData方法已弃用,请使用规则数据管理器');
  1912. var conditionData = {};
  1913. var taskTypes = [];
  1914. // 获取选中的任务类型
  1915. $('input[name="row[task_type][]"]:checked').each(function() {
  1916. taskTypes.push($(this).val());
  1917. });
  1918. // 为每个任务类型收集条件数据
  1919. taskTypes.forEach(function(taskType) {
  1920. var type = parseInt(taskType);
  1921. var condition = {};
  1922. switch(type) {
  1923. case 1: // 购买指定商品
  1924. condition.type = type;
  1925. condition.goods_rule = $('input[name="condition[' + type + '][goods_rule]"]:checked').val() || 1;
  1926. condition.goods_ids = $('#task-goods-ids-' + type).val() || '[]';
  1927. break;
  1928. case 2: // 单笔订单消费满额
  1929. case 3: // 单次充值满额
  1930. case 4: // 活动期间累计消费满额
  1931. condition.type = type;
  1932. condition.condition_value = $('input[name="condition[' + type + '][condition_value]"]').val() || 0;
  1933. break;
  1934. }
  1935. conditionData[type] = condition;
  1936. });
  1937. return {
  1938. conditionData: conditionData,
  1939. taskTypes: taskTypes
  1940. };
  1941. },
  1942. // 选择商品 - 通用方法,支持单选和多选
  1943. selectGoods: function(options) {
  1944. var defaults = {
  1945. mode: 'single', // 'single' 单选, 'multiple' 多选
  1946. title: '选择商品',
  1947. container: '#selected-goods',
  1948. template: 'goods-list-template', // 统一使用goods-list-template
  1949. maxSelect: 0, // 最大选择数量,0表示无限制
  1950. callback: null
  1951. };
  1952. var config = $.extend({}, defaults, options);
  1953. // 构建URL参数
  1954. var urlParams = [];
  1955. // 添加选择模式参数
  1956. if (config.mode === 'single') {
  1957. urlParams.push('selectMode=single');
  1958. } else {
  1959. urlParams.push('selectMode=multiple');
  1960. // 如果有最大选择限制,添加参数
  1961. if (config.maxSelect > 0) {
  1962. urlParams.push('maxSelect=' + config.maxSelect);
  1963. }
  1964. }
  1965. // 构建完整URL
  1966. var url = 'shop/goods/select';
  1967. if (urlParams.length > 0) {
  1968. url += '?' + urlParams.join('&');
  1969. }
  1970. Fast.api.open(url, config.title, {
  1971. callback: function(data) {
  1972. if (data && data.length > 0) {
  1973. // 单选模式只取第一个
  1974. if (config.mode === 'single' && data.length > 1) {
  1975. data = [data[0]];
  1976. }
  1977. Controller.api.updateSelectedGoods(data, config);
  1978. // 执行回调
  1979. if (typeof config.callback === 'function') {
  1980. config.callback(data);
  1981. }
  1982. } else {
  1983. Toastr.error('请选择商品');
  1984. }
  1985. }
  1986. });
  1987. },
  1988. // 选择任务商品(多选模式)
  1989. selectTaskGoods: function(container) {
  1990. Controller.api.selectGoods({
  1991. mode: 'multiple',
  1992. title: '选择参与商品',
  1993. container: container || '#selected-goods',
  1994. template: 'goods-list-template',
  1995. callback: function(data) {
  1996. // 如果是购买指定商品任务,更新隐藏字段和规则数据管理器
  1997. if (container === '#selected-goods' && data && data.length > 0) {
  1998. var goodsIds = data.map(function(item) {
  1999. return item.id;
  2000. });
  2001. // 更新隐藏字段,存储JSON格式的商品IDs
  2002. $('#task-goods-ids-1').val(JSON.stringify(goodsIds));
  2003. console.log('购买指定商品任务 - 商品IDs已更新:', goodsIds);
  2004. // 绑定删除事件
  2005. Controller.api.bindTaskGoodsEvents();
  2006. // 更新规则数据管理器
  2007. Controller.api.updateRulesDataFromDOM();
  2008. }
  2009. }
  2010. });
  2011. },
  2012. // 选择奖品商品(单选模式)
  2013. selectPrizeGoods: function(container) {
  2014. Controller.api.selectGoods({
  2015. mode: 'single',
  2016. title: '选择奖品商品',
  2017. container: container || '#prize-goods-container',
  2018. template: 'goods-list-template' // 使用统一的商品选择模板
  2019. });
  2020. },
  2021. // 更新已选商品
  2022. updateSelectedGoods: function(goods, config) {
  2023. config = config || {};
  2024. var container = config.container || '#selected-goods';
  2025. var template = config.template || 'goods-list-template';
  2026. // 预处理商品数据
  2027. var processedGoods = Controller.api.processGoodsData(goods);
  2028. var html = '';
  2029. if (template === 'goods-list-template') {
  2030. // 商品列表(支持单选和多选)
  2031. html = Template(template, {
  2032. goods: processedGoods
  2033. });
  2034. $(container).html(html);
  2035. // 绑定删除事件
  2036. $('.remove-goods-item').off('click').on('click', function() {
  2037. $(this).closest('.goods-list-item').remove();
  2038. });
  2039. } else if (template === 'prize-goods-template') {
  2040. // 奖品商品(单选)
  2041. if (processedGoods.length > 0) {
  2042. html = Template(template, processedGoods[0]);
  2043. $(container).html(html);
  2044. // 绑定删除事件
  2045. $('.remove-prize-goods').off('click').on('click', function() {
  2046. $(this).closest('.prize-goods-item').remove();
  2047. });
  2048. }
  2049. } else {
  2050. // 其他模板(向后兼容)
  2051. html = Template(template, {
  2052. goods: processedGoods
  2053. });
  2054. $(container).html(html);
  2055. }
  2056. },
  2057. // 处理商品数据
  2058. processGoodsData: function(goods) {
  2059. var processedGoods = [];
  2060. if (goods && goods.length) {
  2061. for (var i = 0; i < goods.length; i++) {
  2062. var item = goods[i];
  2063. var processedItem = {
  2064. id: item.id,
  2065. name: item.name || item.title,
  2066. title: item.title || item.name,
  2067. image: item.image ? (typeof Fast !== 'undefined' && Fast.api ? Fast.api.cdnurl(item.image) : item.image) : '',
  2068. price: item.price || item.sellprice || '',
  2069. goods_sn: item.goods_sn || '',
  2070. spec_type: item.spec_type !== undefined ? item.spec_type : null,
  2071. category: item.category || null,
  2072. type_name: Controller.api.getGoodsTypeName(item),
  2073. type_text: Controller.api.getGoodsTypeText(item)
  2074. };
  2075. processedGoods.push(processedItem);
  2076. }
  2077. }
  2078. return processedGoods;
  2079. },
  2080. // 获取商品类型文本(用于显示标签)
  2081. getGoodsTypeText: function(goods) {
  2082. // 根据商品类型返回相应的标签文本
  2083. if (goods.type == 2) {
  2084. return '密'; // 虚拟商品显示"密"
  2085. }
  2086. if (goods.isrecom == 1) {
  2087. return '荐'; // 推荐商品显示"荐"
  2088. }
  2089. if (goods.ishot == 1) {
  2090. return '热'; // 热门商品显示"热"
  2091. }
  2092. if (goods.isnew == 1) {
  2093. return '新'; // 新品显示"新"
  2094. }
  2095. return ''; // 默认无标签
  2096. },
  2097. // 获取商品类型名称
  2098. getGoodsTypeName: function(goods) {
  2099. // 根据商品类型返回类型名称
  2100. if (goods.type == 1) {
  2101. return '普通商品';
  2102. } else if (goods.type == 2) {
  2103. return '虚拟商品';
  2104. } else if (goods.type == 3) {
  2105. return '服务商品';
  2106. }
  2107. return '普通商品'; // 默认类型
  2108. },
  2109. // 绑定任务商品删除事件
  2110. bindTaskGoodsEvents: function() {
  2111. $('.remove-task-goods').off('click').on('click', function() {
  2112. var goodsId = $(this).data('id');
  2113. var $item = $(this).closest('.goods-list-item');
  2114. // 从DOM中移除商品项
  2115. $item.remove();
  2116. // 更新隐藏字段中的商品IDs
  2117. Controller.api.updateTaskGoodsIds();
  2118. // 更新规则数据管理器
  2119. Controller.api.updateRulesDataFromDOM();
  2120. });
  2121. },
  2122. // 更新任务商品IDs隐藏字段
  2123. updateTaskGoodsIds: function() {
  2124. var goodsIds = [];
  2125. $('#selected-goods .goods-list-item').each(function() {
  2126. var goodsId = $(this).data('id');
  2127. if (goodsId) {
  2128. goodsIds.push(goodsId);
  2129. }
  2130. });
  2131. // 更新隐藏字段
  2132. $('#task-goods-ids-1').val(JSON.stringify(goodsIds));
  2133. console.log('任务商品IDs已更新:', goodsIds);
  2134. },
  2135. // 规则数据管理器 - 类似奖品数据管理器
  2136. rulesDataManager: {
  2137. // 规则数据对象
  2138. rulesData: {},
  2139. // 初始化规则数据管理器
  2140. init: function() {
  2141. // 创建规则数据隐藏域
  2142. if ($('#rules-data-json').length === 0) {
  2143. $('<input>').attr({
  2144. type: 'hidden',
  2145. id: 'rules-data-json',
  2146. name: 'conditions_json',
  2147. value: '{}',
  2148. 'data-rule': 'required;conditionsJson',
  2149. 'data-msg-required': '请设置至少一种参与条件',
  2150. 'data-msg-conditionsJson': '参与条件设置数据格式不正确'
  2151. }).appendTo('form[role="form"]');
  2152. }
  2153. // 初始化数据
  2154. this.loadFromHiddenField();
  2155. },
  2156. // 验证规则数据
  2157. validateRuleData: function(type, ruleData) {
  2158. if (!ruleData.type || ruleData.type !== type) {
  2159. throw new Error('规则类型不匹配');
  2160. }
  2161. switch(parseInt(type)) {
  2162. case 1: // 购买指定商品
  2163. var goodsIds = JSON.parse(ruleData.goods_ids || '[]');
  2164. if (!goodsIds || goodsIds.length === 0) {
  2165. throw new Error('请选择参与商品');
  2166. }
  2167. if (!ruleData.goods_rule || (ruleData.goods_rule != 1 && ruleData.goods_rule != 2)) {
  2168. throw new Error('请选择商品规则');
  2169. }
  2170. break;
  2171. case 2: // 单笔订单消费满额
  2172. case 3: // 单次充值满额
  2173. case 4: // 活动期间累计消费满额
  2174. if (!ruleData.condition_value || parseFloat(ruleData.condition_value) <= 0) {
  2175. throw new Error('请输入有效的条件金额');
  2176. }
  2177. break;
  2178. default:
  2179. throw new Error('无效的规则类型');
  2180. }
  2181. return true;
  2182. },
  2183. // 从隐藏域加载数据
  2184. loadFromHiddenField: function() {
  2185. var jsonData = $('#rules-data-json').val();
  2186. try {
  2187. this.rulesData = JSON.parse(jsonData || '{}');
  2188. } catch (e) {
  2189. this.rulesData = {};
  2190. console.warn('规则数据格式错误,已重置为空对象');
  2191. }
  2192. },
  2193. // 保存数据到隐藏域
  2194. saveToHiddenField: function() {
  2195. var jsonData = JSON.stringify(this.rulesData);
  2196. $('#rules-data-json').val(jsonData);
  2197. console.log('规则数据已保存到隐藏域:', this.rulesData);
  2198. },
  2199. // 添加或更新规则数据
  2200. updateRule: function(type, ruleData) {
  2201. // 验证规则数据
  2202. this.validateRuleData(type, ruleData);
  2203. this.rulesData[type] = ruleData;
  2204. this.saveToHiddenField();
  2205. },
  2206. // 删除规则
  2207. deleteRule: function(type) {
  2208. delete this.rulesData[type];
  2209. this.saveToHiddenField();
  2210. },
  2211. // 获取规则数据
  2212. getRule: function(type) {
  2213. return this.rulesData[type] || null;
  2214. },
  2215. // 获取所有规则数据
  2216. getAllRules: function() {
  2217. return this.rulesData;
  2218. },
  2219. // 检查是否有规则数据
  2220. hasRules: function() {
  2221. return Object.keys(this.rulesData).length > 0;
  2222. },
  2223. // 清空所有规则
  2224. clearAllRules: function() {
  2225. this.rulesData = {};
  2226. this.saveToHiddenField();
  2227. }
  2228. },
  2229. // 收集规则数据并保存到隐藏域
  2230. collectAndSaveRulesData: function() {
  2231. var rulesData = {};
  2232. var taskTypes = [];
  2233. // 获取选中的任务类型
  2234. $('input[name="row[task_type][]"]:checked').each(function() {
  2235. taskTypes.push($(this).val());
  2236. });
  2237. // 为每个任务类型收集规则数据
  2238. taskTypes.forEach(function(taskType) {
  2239. var type = parseInt(taskType);
  2240. var rule = {
  2241. type: type
  2242. };
  2243. switch(type) {
  2244. case 1: // 购买指定商品
  2245. rule.goods_rule = $('input[name="condition[' + type + '][goods_rule]"]:checked').val() || 1;
  2246. rule.goods_ids = $('#task-goods-ids-' + type).val() || '[]';
  2247. break;
  2248. case 2: // 单笔订单消费满额
  2249. case 3: // 单次充值满额
  2250. case 4: // 活动期间累计消费满额
  2251. rule.condition_value = $('input[name="condition[' + type + '][condition_value]"]').val() || 0;
  2252. break;
  2253. }
  2254. rulesData[type] = rule;
  2255. });
  2256. // 保存到规则数据管理器
  2257. Controller.api.rulesDataManager.rulesData = rulesData;
  2258. Controller.api.rulesDataManager.saveToHiddenField();
  2259. console.log('规则数据已收集并保存:', rulesData);
  2260. },
  2261. // 从DOM更新规则数据
  2262. updateRulesDataFromDOM: function() {
  2263. // 在任务类型变化时同步更新规则数据
  2264. Controller.api.collectAndSaveRulesData();
  2265. }
  2266. }
  2267. };
  2268. // 全局函数,供HTML调用
  2269. window.addPrize = function() {
  2270. Controller.api.addPrize();
  2271. };
  2272. // 默认商品选择(针对购买指定商品任务,使用多选模式)
  2273. window.selectGoods = function() {
  2274. Controller.api.selectGoods({
  2275. mode: 'multiple',
  2276. title: '选择参与商品',
  2277. container: '#selected-goods',
  2278. template: 'goods-list-template'
  2279. });
  2280. };
  2281. // 任务商品选择(多选)
  2282. window.selectTaskGoods = function(container) {
  2283. Controller.api.selectTaskGoods(container);
  2284. };
  2285. // 奖品商品选择(单选)
  2286. window.selectPrizeGoods = function(container) {
  2287. Controller.api.selectPrizeGoods(container);
  2288. };
  2289. // 暴露奖品数据管理器到全局
  2290. window.Controller = Controller;
  2291. return Controller;
  2292. });