index.vue 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496
  1. <template>
  2. <div class="app-container">
  3. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
  4. <el-form-item label="奖励名称" prop="name">
  5. <el-input
  6. v-model="queryParams.name"
  7. placeholder="请输入奖励名称"
  8. clearable
  9. size="small"
  10. @keyup.enter.native="handleQuery"
  11. />
  12. </el-form-item>
  13. <el-form-item label="奖励描述" prop="description">
  14. <el-input
  15. v-model="queryParams.description"
  16. placeholder="请输入奖励描述"
  17. clearable
  18. size="small"
  19. @keyup.enter.native="handleQuery"
  20. />
  21. </el-form-item>
  22. <el-form-item label="奖励类型" prop="rewardType">
  23. <el-select v-model="queryParams.rewardType" placeholder="请选择奖励类型" clearable size="small">
  24. <el-option
  25. v-for="item in typeOptions"
  26. :key="item.dictValue"
  27. :label="item.dictLabel"
  28. :value="item.dictValue"
  29. />
  30. </el-select>
  31. </el-form-item>
  32. <el-form-item>
  33. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  34. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  35. </el-form-item>
  36. </el-form>
  37. <el-row :gutter="10" class="mb8">
  38. <el-col :span="1.5">
  39. <el-button
  40. type="primary"
  41. plain
  42. icon="el-icon-plus"
  43. size="mini"
  44. @click="handleAdd"
  45. v-hasPermi="['course:reward:add']"
  46. >新增</el-button>
  47. </el-col>
  48. <el-col :span="1.5">
  49. <el-button
  50. type="success"
  51. plain
  52. icon="el-icon-edit"
  53. size="mini"
  54. :disabled="single"
  55. @click="handleUpdate"
  56. v-hasPermi="['course:reward:edit']"
  57. >修改</el-button>
  58. </el-col>
  59. <el-col :span="1.5">
  60. <el-button
  61. type="danger"
  62. plain
  63. icon="el-icon-delete"
  64. size="mini"
  65. :disabled="multiple"
  66. @click="handleDelete"
  67. v-hasPermi="['course:reward:remove']"
  68. >删除</el-button>
  69. </el-col>
  70. <el-col :span="1.5">
  71. <el-button
  72. type="warning"
  73. plain
  74. icon="el-icon-download"
  75. size="mini"
  76. :loading="exportLoading"
  77. @click="handleExport"
  78. v-hasPermi="['course:reward:export']"
  79. >导出</el-button>
  80. </el-col>
  81. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  82. </el-row>
  83. <el-table border v-loading="loading" :data="rewardList" @selection-change="handleSelectionChange">
  84. <el-table-column type="selection" width="55" align="center" />
  85. <el-table-column label="主键ID" align="center" prop="id" />
  86. <el-table-column label="奖励名称" align="center" prop="name" />
  87. <el-table-column label="奖励描述" align="center" prop="description" />
  88. <el-table-column label="奖励类型" align="center" prop="rewardType">
  89. <template slot-scope="scope">
  90. <dict-tag :options="typeOptions" :value="scope.row.rewardType"/>
  91. </template>
  92. </el-table-column>
  93. <el-table-column label="状态" align="center" prop="status">
  94. <template slot-scope="scope">
  95. <dict-tag :options="statusOptions" :value="scope.row.status"/>
  96. </template>
  97. </el-table-column>
  98. <el-table-column label="期望值" align="center" prop="expectedValue" />
  99. <el-table-column label="实际奖励内容" align="center" prop="actualRewards">
  100. <template slot-scope="scope">
  101. <el-button
  102. size="mini"
  103. type="text"
  104. icon="el-icon-view"
  105. @click="handleViewReward(scope.row)"
  106. >查看详情</el-button>
  107. </template>
  108. </el-table-column>
  109. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  110. <template slot-scope="scope">
  111. <el-button
  112. size="mini"
  113. type="text"
  114. icon="el-icon-edit"
  115. @click="handleUpdate(scope.row)"
  116. v-hasPermi="['course:reward:edit']"
  117. >修改</el-button>
  118. <el-button
  119. size="mini"
  120. type="text"
  121. icon="el-icon-delete"
  122. @click="handleDelete(scope.row)"
  123. v-hasPermi="['course:reward:remove']"
  124. >删除</el-button>
  125. </template>
  126. </el-table-column>
  127. </el-table>
  128. <pagination
  129. v-show="total>0"
  130. :total="total"
  131. :page.sync="queryParams.pageNum"
  132. :limit.sync="queryParams.pageSize"
  133. @pagination="getList"
  134. />
  135. <!-- 添加或修改奖励配置对话框 -->
  136. <el-dialog :title="title" :visible.sync="open" :width="width" append-to-body>
  137. <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  138. <el-form-item label="奖励名称" prop="name">
  139. <el-input v-model="form.name" placeholder="请输入奖励名称" @input="updateRewardItemsName" />
  140. </el-form-item>
  141. <el-form-item label="奖励类型" prop="rewardType">
  142. <el-select v-model="form.rewardType" placeholder="请选择奖励类型" @change="handleRewardTypeChange">
  143. <el-option
  144. v-for="item in typeOptions"
  145. :key="item.dictValue"
  146. :label="item.dictLabel"
  147. :value="item.dictValue"
  148. />
  149. </el-select>
  150. </el-form-item>
  151. <el-form-item label="期望值" prop="expectedValue">
  152. <el-input-number :min="0" :precision="2" :step="1" v-model="form.expectedValue" placeholder="期望值" />
  153. </el-form-item>
  154. <el-form-item v-if="form.rewardType === '1'" label="宝箱图片">
  155. <div class="image-upload-group">
  156. <div class="image-upload-item">
  157. <span>关闭图片</span>
  158. <el-upload
  159. v-model="form.closeChestUrl"
  160. class="avatar-uploader"
  161. :action="uploadUrl"
  162. :show-file-list="false"
  163. :on-success="handleSuccessClose"
  164. :before-upload="beforeUpload">
  165. <img v-if="form.closeChestUrl" :src="form.closeChestUrl" class="avatar">
  166. <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  167. </el-upload>
  168. </div>
  169. <div class="image-upload-item">
  170. <span>开启图片</span>
  171. <el-upload
  172. v-model="form.openChestUrl"
  173. class="avatar-uploader"
  174. :action="uploadUrl"
  175. :show-file-list="false"
  176. :on-success="handleSuccessOpen"
  177. :before-upload="beforeUpload">
  178. <img v-if="form.openChestUrl" :src="form.openChestUrl" class="avatar">
  179. <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  180. </el-upload>
  181. </div>
  182. </div>
  183. </el-form-item>
  184. <el-form-item v-if="form.rewardType === '2'"
  185. label="红包金额" prop="account">
  186. <el-input-number v-model="form.account" :min="0" :step="0.1" style="width: 200px;"></el-input-number>
  187. </el-form-item>
  188. <el-form-item v-if="form.rewardType === '3'"
  189. label="福币数" prop="account">
  190. <el-input-number v-model="form.account" :min="0" :step="1" style="width: 200px;"></el-input-number>
  191. </el-form-item>
  192. <el-form-item v-if="form.rewardType === '1'" label="宝箱奖励" prop="rewardItems">
  193. <div class="reward-table-container">
  194. <div class="table-header">
  195. <span>宝箱奖励配置</span>
  196. <el-button type="primary" icon="el-icon-plus" size="mini" @click="addRewardItem">添加奖励项</el-button>
  197. </div>
  198. <el-table :data="form.rewardItems" border size="small">
  199. <el-table-column v-if="false" label="奖励名称" >
  200. <template slot-scope="scope">
  201. <el-input v-model="scope.row.name" :placeholder="form.name || '请输入奖励名称'"></el-input>
  202. </template>
  203. </el-table-column>
  204. <el-table-column label="奖励模式">
  205. <template slot-scope="scope">
  206. <el-select v-model="scope.row.jltype" disabled placeholder="请选择奖励模式" style="width: 100%">
  207. <el-option
  208. v-for="item in jltypeOptions"
  209. :key="item.dictValue"
  210. :label="item.dictLabel"
  211. :value="item.dictValue"
  212. />
  213. </el-select>
  214. </template>
  215. </el-table-column>
  216. <el-table-column label="奖励数量">
  217. <template slot-scope="scope">
  218. <el-input-number v-model="scope.row.amount" :min="1" :controls="false" style="width: 100%"></el-input-number>
  219. </template>
  220. </el-table-column>
  221. <el-table-column label="概率" >
  222. <template slot-scope="scope">
  223. <el-input v-model="scope.row.probability" placeholder="例如: 20%"></el-input>
  224. </template>
  225. </el-table-column>
  226. <el-table-column v-if="false" label="唯一序列号" >
  227. <template slot-scope="scope">
  228. <el-input v-model="scope.row.code" placeholder="奖励唯一序列号">
  229. <el-button slot="append" icon="el-icon-refresh" @click="generateUUID(scope.row)"></el-button>
  230. </el-input>
  231. </template>
  232. </el-table-column>
  233. <el-table-column label="操作" width="80">
  234. <template slot-scope="scope">
  235. <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeRewardItem(scope.$index)"></el-button>
  236. </template>
  237. </el-table-column>
  238. </el-table>
  239. </div>
  240. </el-form-item>
  241. <el-form-item v-if="form.rewardType === '4'" label="转盘奖励" prop="rewardItems">
  242. <div class="reward-table-container spin-reward">
  243. <div class="table-header">
  244. <span>转盘奖励配置</span>
  245. <el-button type="primary" icon="el-icon-plus" @click="addSpinRewardItem">添加奖励项</el-button>
  246. </div>
  247. <div class="spin-tips">建议:概率填写为百分比字符串,如 12.5% ,各项之和=100%</div>
  248. <el-table :data="form.rewardItems" border stripe>
  249. <el-table-column label="图标" align="center" width="72">
  250. <template slot-scope="scope">
  251. <el-upload
  252. class="spin-icon-uploader"
  253. :action="uploadUrl"
  254. :show-file-list="false"
  255. :before-upload="beforeUpload"
  256. accept="image/*"
  257. :on-success="(res,file)=>handleSpinIconSuccess(res, scope.row)"
  258. >
  259. <img v-if="scope.row.iconUrl" :src="scope.row.iconUrl" class="spin-icon" />
  260. <i v-else class="el-icon-plus avatar-uploader-icon spin-icon"></i>
  261. </el-upload>
  262. </template>
  263. </el-table-column>
  264. <el-table-column label="奖励名称" align="center">
  265. <template slot-scope="scope">
  266. <el-input v-model="scope.row.name" placeholder="请输入奖励名称"></el-input>
  267. </template>
  268. </el-table-column>
  269. <el-table-column label="奖励类型" align="center" width="120">
  270. <template slot-scope="scope">
  271. <el-select v-model="scope.row.type" @change="changeType(scope.row)" placeholder="选择类型" style="width: 100%">
  272. <el-option
  273. v-for="opt in spinItemTypeOptions"
  274. :key="opt.dictValue"
  275. :label="opt.dictLabel"
  276. :value="opt.dictValue"
  277. />
  278. </el-select>
  279. </template>
  280. </el-table-column>
  281. <el-table-column label="优惠券" align="center">
  282. <template slot-scope="scope">
  283. <el-select
  284. :disabled="scope.row.type !== '4'"
  285. :ref="`customSelect_${scope.row.code}`"
  286. v-model="scope.row.couponId"
  287. placeholder="请选择优惠券"
  288. @click.native.stop="scope.row.type === '4' ? openCouponDrawer(scope.row) : null"
  289. clearable
  290. style="width: 100%;">
  291. <el-option
  292. v-for="item in couponList"
  293. :key="item.id"
  294. :label="item.name"
  295. :value="item.id">
  296. </el-option>
  297. </el-select>
  298. </template>
  299. </el-table-column>
  300. <el-table-column label="商品" align="center">
  301. <template slot-scope="scope">
  302. <el-select
  303. :disabled="scope.row.type !== '5'"
  304. :ref="`customSelect_${scope.row.code}`"
  305. v-model="scope.row.goodsId"
  306. placeholder="请选择商品"
  307. @click.native.stop="scope.row.type === '5' ? openGoodsDrawer(scope.row) : null"
  308. clearable
  309. style="width: 100%;">
  310. <el-option
  311. v-for="item in goodsList"
  312. :key="item.id"
  313. :label="item.name"
  314. :value="item.id">
  315. </el-option>
  316. </el-select>
  317. </template>
  318. </el-table-column>
  319. <el-table-column label="奖励数量" align="center" show-overflow-tooltip width="180">
  320. <template slot-scope="scope">
  321. <el-input :disabled="scope.row.type === '5'" v-model="scope.row.amount" :controls="false" style="width: 100%"/>
  322. </template>
  323. </el-table-column>
  324. <el-table-column label="概率" align="center" class-name="probability-col" width="120">
  325. <template slot-scope="scope">
  326. <el-input v-model="scope.row.probability" placeholder="例如: 10%"></el-input>
  327. </template>
  328. </el-table-column>
  329. <el-table-column v-if="false" label="唯一序列号">
  330. <template slot-scope="scope">
  331. <el-input v-model="scope.row.code" placeholder="奖励唯一序列号">
  332. <el-button slot="append" icon="el-icon-refresh" @click="generateUUID(scope.row)"></el-button>
  333. </el-input>
  334. </template>
  335. </el-table-column>
  336. <el-table-column label="操作" align="center" width="80">
  337. <template slot-scope="scope">
  338. <el-button type="danger" icon="el-icon-delete" @click="removeRewardItem(scope.$index)"></el-button>
  339. </template>
  340. </el-table-column>
  341. </el-table>
  342. </div>
  343. </el-form-item>
  344. <el-form-item v-if="form.rewardType === '5'" label="保底转盘" prop="rewardItems">
  345. <div class="reward-table-container spin-reward">
  346. <div class="table-header">
  347. <span>保底转盘奖励配置</span>
  348. <el-button type="primary" icon="el-icon-plus" @click="addSpinRewardItem2">添加奖励项</el-button>
  349. </div>
  350. <div class="spin-tips">
  351. 建议:概率填写为百分比字符串,如 12.5% ,各项之和=100%
  352. <br/>
  353. 保底:保底奖励只能有1个且最后5次才能抽中
  354. </div>
  355. <el-table :data="form.rewardItems" border stripe>
  356. <el-table-column label="图标" align="center" width="72">
  357. <template slot-scope="scope">
  358. <el-upload
  359. class="spin-icon-uploader"
  360. :action="uploadUrl"
  361. :show-file-list="false"
  362. :before-upload="beforeUpload"
  363. accept="image/*"
  364. :on-success="(res,file)=>handleSpinIconSuccess(res, scope.row)"
  365. >
  366. <img v-if="scope.row.iconUrl" :src="scope.row.iconUrl" class="spin-icon" />
  367. <i v-else class="el-icon-plus avatar-uploader-icon spin-icon"></i>
  368. </el-upload>
  369. </template>
  370. </el-table-column>
  371. <el-table-column label="奖励名称" align="center">
  372. <template slot-scope="scope">
  373. <el-input v-model="scope.row.name" placeholder="请输入奖励名称"></el-input>
  374. </template>
  375. </el-table-column>
  376. <el-table-column label="奖励类型" align="center" width="120">
  377. <template slot-scope="scope">
  378. <el-select v-model="scope.row.type" @change="changeType(scope.row)" placeholder="选择类型" style="width: 100%">
  379. <el-option
  380. v-for="opt in spinItemTypeOptions"
  381. :key="opt.dictValue"
  382. :label="opt.dictLabel"
  383. :value="opt.dictValue"
  384. />
  385. </el-select>
  386. </template>
  387. </el-table-column>
  388. <el-table-column label="优惠券" align="center">
  389. <template slot-scope="scope">
  390. <el-select
  391. :disabled="scope.row.type !== '4'"
  392. :ref="`customSelect_${scope.row.code}`"
  393. v-model="scope.row.couponId"
  394. placeholder="请选择优惠券"
  395. @click.native.stop="scope.row.type === '4' ? openCouponDrawer(scope.row) : null"
  396. clearable
  397. style="width: 100%;">
  398. <el-option
  399. v-for="item in couponList"
  400. :key="item.id"
  401. :label="item.name"
  402. :value="item.id">
  403. </el-option>
  404. </el-select>
  405. </template>
  406. </el-table-column>
  407. <el-table-column label="商品" align="center">
  408. <template slot-scope="scope">
  409. <el-select
  410. :disabled="scope.row.type !== '5'"
  411. :ref="`customSelect_${scope.row.code}`"
  412. v-model="scope.row.goodsId"
  413. placeholder="请选择商品"
  414. @click.native.stop="scope.row.type === '5' ? openGoodsDrawer(scope.row) : null"
  415. clearable
  416. style="width: 100%;">
  417. <el-option
  418. v-for="item in goodsList"
  419. :key="item.id"
  420. :label="item.name"
  421. :value="item.id">
  422. </el-option>
  423. </el-select>
  424. </template>
  425. </el-table-column>
  426. <el-table-column label="奖励数量" align="center" width="180">
  427. <template slot-scope="scope">
  428. <el-input :disabled="scope.row.type === '5'" v-model="scope.row.amount" :controls="false" style="width: 100%"/>
  429. </template>
  430. </el-table-column>
  431. <el-table-column label="概率" align="center" class-name="probability-col" width="120">
  432. <template slot-scope="scope">
  433. <el-input v-model="scope.row.probability" placeholder="例如: 10%"></el-input>
  434. </template>
  435. </el-table-column>
  436. <el-table-column label="保底" align="center" width="100">
  437. <template slot-scope="scope">
  438. <el-switch
  439. v-model="scope.row.isGuarantee"
  440. active-color="#13ce66">
  441. </el-switch>
  442. </template>
  443. </el-table-column>
  444. <el-table-column v-if="false" label="唯一序列号">
  445. <template slot-scope="scope">
  446. <el-input v-model="scope.row.code" placeholder="奖励唯一序列号">
  447. <el-button slot="append" icon="el-icon-refresh" @click="generateUUID(scope.row)"></el-button>
  448. </el-input>
  449. </template>
  450. </el-table-column>
  451. <el-table-column label="操作" align="center" width="80">
  452. <template slot-scope="scope">
  453. <el-button type="danger" icon="el-icon-delete" @click="removeRewardItem(scope.$index)"></el-button>
  454. </template>
  455. </el-table-column>
  456. </el-table>
  457. </div>
  458. </el-form-item>
  459. <el-form-item label="状态" prop="status">
  460. <el-radio-group v-model="form.status">
  461. <el-radio v-for="dict in statusOptions" :key="dict.dictValue" :label="dict.dictValue">{{dict.dictLabel}}</el-radio>
  462. </el-radio-group>
  463. </el-form-item>
  464. <el-form-item label="奖励描述" prop="description">
  465. <el-input v-model="form.description" type="textarea" placeholder="请输入奖励描述" />
  466. </el-form-item>
  467. </el-form>
  468. <div slot="footer" class="dialog-footer">
  469. <el-button type="primary" @click="submitForm">确 定</el-button>
  470. <el-button @click="cancel">取 消</el-button>
  471. </div>
  472. </el-dialog>
  473. <el-dialog
  474. :title="`奖励详情 - ${currentReward.name || '未知奖励'}`"
  475. :visible.sync="detailVisible"
  476. width="700px"
  477. append-to-body
  478. >
  479. <div class="reward-detail-container">
  480. <!-- 基础信息 -->
  481. <el-descriptions :column="2" border class="base-info" :label-style="{ width: '120px' }" :content-style="{ width: '200px' }">
  482. <el-descriptions-item label="奖励名称">{{ currentReward.name || '-' }}</el-descriptions-item>
  483. <el-descriptions-item label="奖励类型">
  484. <dict-tag :options="typeOptions" :value="currentReward.rewardType"/>
  485. </el-descriptions-item>
  486. <el-descriptions-item label="状态">
  487. <dict-tag :options="statusOptions" :value="currentReward.status"/>
  488. </el-descriptions-item>
  489. <el-descriptions-item label="期望值">{{ currentReward.expectedValue || 0 }}</el-descriptions-item>
  490. <el-descriptions-item label="描述" :span="2">{{ currentReward.description || '-' }}</el-descriptions-item>
  491. </el-descriptions>
  492. <!-- 奖励内容详情 -->
  493. <div class="reward-content">
  494. <h4>奖励内容详情</h4>
  495. <!-- 宝箱类型奖励 -->
  496. <div v-if="currentReward.rewardType === 1" class="chest-reward">
  497. <el-table :data="parsedRewardItems" size="small" border stripe>
  498. <el-table-column label="奖励模式">
  499. <template slot-scope="{row}">
  500. <el-select v-model="row.jltype" placeholder="请选择奖励模式" style="width: 100%" disabled>
  501. <el-option
  502. v-for="item in jltypeOptions"
  503. :key="item.dictValue"
  504. :label="item.dictLabel"
  505. :value="item.dictValue"
  506. />
  507. </el-select>
  508. </template>
  509. </el-table-column>
  510. <el-table-column label="数量" prop="amount" align="center">
  511. <template slot-scope="{row}">
  512. {{ row.amount || 1 }}
  513. </template>
  514. </el-table-column>
  515. <el-table-column label="概率" prop="probability" align="center">
  516. <template slot-scope="{row}">
  517. <el-tag v-if="row.probability" size="small">{{ row.probability }}</el-tag>
  518. <span v-else>-</span>
  519. </template>
  520. </el-table-column>
  521. </el-table>
  522. <div v-if="!parsedRewardItems || parsedRewardItems.length === 0" class="empty-tip">
  523. 暂无奖励配置
  524. </div>
  525. </div>
  526. <!-- 红包类型奖励 -->
  527. <div v-else-if="currentReward.rewardType === 2" class="redpacket-reward">
  528. <div class="reward-amount">
  529. <i class="el-icon-money" style="color: #e6a23c; font-size: 24px;"></i>
  530. <span class="amount-text">{{ rewardAmount }} 元</span>
  531. <el-tag type="warning" size="small">红包奖励</el-tag>
  532. </div>
  533. </div>
  534. <!-- 芳华币类型奖励 -->
  535. <div v-else-if="currentReward.rewardType === 3" class="points-reward">
  536. <div class="reward-amount">
  537. <i class="el-icon-coin" style="color: #67c23a; font-size: 24px;"></i>
  538. <span class="amount-text">{{ rewardAmount }} 福币</span>
  539. <el-tag type="success" size="small">福币奖励</el-tag>
  540. </div>
  541. </div>
  542. <!-- 转盘类型奖励 -->
  543. <div v-else-if="currentReward.rewardType === 4" class="chest-reward">
  544. <el-table :data="parsedRewardItems" size="small" border stripe>
  545. <el-table-column label="图标" prop="iconUrl" align="center" width="80">
  546. <template slot-scope="{row}">
  547. <el-image
  548. v-if="row.iconUrl"
  549. :src="row.iconUrl"
  550. :preview-src-list="[row.iconUrl]"
  551. fit="cover"
  552. style="width:32px;height:32px;border:1px solid #ebeef5;border-radius:4px;"
  553. />
  554. <span v-else>-</span>
  555. </template>
  556. </el-table-column>
  557. <el-table-column label="奖品名称" prop="name" align="center" />
  558. <el-table-column label="奖品类型" prop="type" align="center">
  559. <template slot-scope="{row}">
  560. <el-tag v-if="row.type" size="small">
  561. {{ getSpinItemTypeLabel(row.type) }}
  562. </el-tag>
  563. <span v-else>-</span>
  564. </template>
  565. </el-table-column>
  566. <el-table-column label="数量" prop="amount" align="center">
  567. <template slot-scope="{row}">
  568. <span v-if="row.amount">
  569. {{ row.amount }}
  570. </span>
  571. <span v-else>-</span>
  572. </template>
  573. </el-table-column>
  574. <el-table-column label="概率" prop="probability" align="center">
  575. <template slot-scope="{row}">
  576. <el-tag v-if="row.probability" size="small">{{ row.probability }}</el-tag>
  577. <span v-else>-</span>
  578. </template>
  579. </el-table-column>
  580. </el-table>
  581. <div v-if="!parsedRewardItems || parsedRewardItems.length === 0" class="empty-tip">
  582. 暂无奖励配置
  583. </div>
  584. </div>
  585. <!-- 保底转盘类型奖励 -->
  586. <div v-else-if="currentReward.rewardType === 5" class="chest-reward">
  587. <el-table :data="parsedRewardItems" size="small" border stripe>
  588. <el-table-column label="图标" prop="iconUrl" align="center" width="80">
  589. <template slot-scope="{row}">
  590. <el-image
  591. v-if="row.iconUrl"
  592. :src="row.iconUrl"
  593. :preview-src-list="[row.iconUrl]"
  594. fit="cover"
  595. style="width:32px;height:32px;border:1px solid #ebeef5;border-radius:4px;"
  596. />
  597. <span v-else>-</span>
  598. </template>
  599. </el-table-column>
  600. <el-table-column label="奖品名称" prop="name" align="center" />
  601. <el-table-column label="奖品类型" prop="type" align="center">
  602. <template slot-scope="{row}">
  603. <el-tag v-if="row.type" size="small">
  604. {{ getSpinItemTypeLabel(row.type) }}
  605. </el-tag>
  606. <span v-else>-</span>
  607. </template>
  608. </el-table-column>
  609. <el-table-column label="数量" prop="amount" align="center">
  610. <template slot-scope="{row}">
  611. <span v-if="row.amount">
  612. {{ row.amount }}
  613. </span>
  614. <span v-else>-</span>
  615. </template>
  616. </el-table-column>
  617. <el-table-column label="概率" prop="probability" align="center">
  618. <template slot-scope="{row}">
  619. <el-tag v-if="row.probability" size="small">{{ row.probability }}</el-tag>
  620. <span v-else>-</span>
  621. </template>
  622. </el-table-column>
  623. <el-table-column label="保底" prop="isGuarantee" align="center">
  624. <template slot-scope="{row}">
  625. <el-tag size="small" :type="row.isGuarantee ? 'success' : 'info'">{{ row.isGuarantee ? '是' : '否' }}</el-tag>
  626. </template>
  627. </el-table-column>
  628. </el-table>
  629. <div v-if="!parsedRewardItems || parsedRewardItems.length === 0" class="empty-tip">
  630. 暂无奖励配置
  631. </div>
  632. </div>
  633. <!-- 未知类型 -->
  634. <div v-else class="unknown-reward">
  635. <el-alert type="info" title="未知奖励类型" :closable="false"></el-alert>
  636. </div>
  637. </div>
  638. </div>
  639. <div slot="footer" class="dialog-footer">
  640. <el-button @click="detailVisible = false">关 闭</el-button>
  641. </div>
  642. </el-dialog>
  643. <!-- 选择优惠券 -->
  644. <el-drawer :append-to-body="true" :with-header="false" size="75%" :visible.sync="couponDrawerOpen">
  645. <coupon-component @select-coupon="selectCoupon"></coupon-component>
  646. </el-drawer>
  647. <!-- &lt;!&ndash; 选择商品 &ndash;&gt;-->
  648. <!-- <el-drawer :append-to-body="true" :with-header="false" size="75%" :visible.sync="goodsDrawerOpen">-->
  649. <!-- <RewardGoodsComponent @select-goods="selectGoods"></RewardGoodsComponent>-->
  650. <!-- </el-drawer>-->
  651. </div>
  652. </template>
  653. <script>
  654. import { listReward, getReward, delReward, addReward, updateReward, exportReward } from "@/api/course/reward";
  655. import CouponComponent from '@/views/components/his/couponComponent.vue'
  656. // import RewardGoodsComponent from '@/views/components/course/rewardGoodsComponent.vue'
  657. import {Loading} from "element-ui";
  658. import { getByIds } from '@/api/his/coupon'
  659. // import { getGoodsByIds } from '@/api/reward/rewardGoods'
  660. export default {
  661. name: "Reward",
  662. components: {
  663. CouponComponent
  664. },
  665. data() {
  666. return {
  667. finalQuality:1,
  668. uploadUrl: process.env.VUE_APP_BASE_API+"/common/uploadOSS",
  669. detailVisible: false,
  670. currentReward: {},
  671. activeCollapse: [],
  672. jltypeOptions: [],
  673. // 遮罩层
  674. loading: true,
  675. // 导出遮罩层
  676. exportLoading: false,
  677. // 选中数组
  678. ids: [],
  679. // 非单个禁用
  680. single: true,
  681. // 非多个禁用
  682. multiple: true,
  683. // 显示搜索条件
  684. showSearch: true,
  685. // 总条数
  686. total: 0,
  687. rewardList: [],
  688. typeOptions: [],
  689. // 转盘奖励-奖励类型选项
  690. spinItemTypeOptions: [],
  691. statusOptions: [],
  692. // 弹出层标题
  693. title: "",
  694. // 是否显示弹出层
  695. open: false,
  696. width: '1200px',
  697. // 查询参数
  698. queryParams: {
  699. pageNum: 1,
  700. pageSize: 10,
  701. name: null,
  702. description: null,
  703. rewardType: null,
  704. status: null,
  705. expectedValue: null,
  706. createId: null,
  707. actualRewards: null
  708. },
  709. // 已选中优惠券列表
  710. couponList: [],
  711. couponDrawerOpen: false,
  712. currentRow: null,
  713. // 已选中商品列表
  714. goodsList: [],
  715. goodsDrawerOpen: false,
  716. // 表单参数
  717. form: {
  718. id: null,
  719. name: null,
  720. description: null,
  721. rewardType: null,
  722. status: "1",
  723. expectedValue: 0,
  724. account: 0,
  725. rewardItems: [],
  726. openChestUrl:"",
  727. closeChestUrl:"",
  728. },
  729. // 表单校验
  730. rules: {
  731. name: [
  732. { required: true, message: "奖励名称不能为空", trigger: "blur" }
  733. ],
  734. rewardType: [
  735. { required: true, message: "奖励类型不能为空", trigger: "change" }
  736. ],
  737. status: [
  738. { required: true, message: "状态 不能为空", trigger: "blur" }
  739. ]
  740. }
  741. };
  742. },
  743. computed: {
  744. // 解析奖励项数据
  745. parsedRewardItems() {
  746. if (!this.currentReward.actualRewards) return null;
  747. try {
  748. const parsed = JSON.parse(this.currentReward.actualRewards);
  749. return Array.isArray(parsed) ? parsed : null;
  750. } catch (e) {
  751. console.error("解析奖励内容失败", e);
  752. return null;
  753. }
  754. },
  755. // 获取红包或芳华币金额
  756. rewardAmount() {
  757. if (!this.currentReward.actualRewards) return 0;
  758. try {
  759. const parsed = JSON.parse(this.currentReward.actualRewards);
  760. if (this.currentReward.rewardType === 2) {
  761. return parsed.amount || parsed.money || 0;
  762. } else if (this.currentReward.rewardType === 3) {
  763. return parsed.points || parsed.score || 0;
  764. }
  765. return 0;
  766. } catch (e) {
  767. return 0;
  768. }
  769. }
  770. },
  771. created() {
  772. this.getList();
  773. this.getDicts("sys_reward_type").then((response) => {
  774. this.typeOptions = response.data;
  775. });
  776. this.getDicts("sys_user_status").then((response) => {
  777. this.statusOptions = response.data;
  778. });
  779. this.getDicts("sys_reward_spin_type").then((response) => {
  780. this.jltypeOptions = response.data;
  781. });
  782. this.getDicts("spin_reward_type").then((response) => {
  783. this.spinItemTypeOptions = response.data;
  784. });
  785. },
  786. methods: {
  787. changeType(row) {
  788. row.amount = null
  789. row.couponId = null
  790. row.goodsId = null
  791. if (row.type === '5') {
  792. row.amount = 1
  793. }
  794. },
  795. openCouponDrawer(row) {
  796. this.$nextTick(() => {
  797. this.$refs[`customSelect_${row.code}`]?.blur?.()
  798. });
  799. this.currentRow = row
  800. this.couponDrawerOpen = true
  801. },
  802. selectCoupon(coupon) {
  803. this.currentRow.couponId = coupon.id
  804. if (!this.couponList.some(item => item.id === coupon.id)) {
  805. this.couponList.push(coupon)
  806. }
  807. this.couponDrawerOpen = false
  808. },
  809. openGoodsDrawer(row) {
  810. this.$nextTick(() => {
  811. this.$refs[`customSelect_${row.code}`]?.blur?.()
  812. });
  813. this.currentRow = row
  814. this.goodsDrawerOpen = true
  815. },
  816. selectGoods(goods) {
  817. this.currentRow.goodsId = goods.id
  818. if (!this.goodsList.some(item => item.id === goods.id)) {
  819. this.goodsList.push(goods)
  820. }
  821. this.goodsDrawerOpen = false
  822. },
  823. handleSuccessOpen(res, file) {
  824. if(res.code==200){
  825. this.form.openChestUrl = res.url;
  826. }
  827. else{
  828. this.msgError(res.msg);
  829. }
  830. },
  831. handleSuccessClose(res, file) {
  832. if(res.code==200){
  833. this.form.closeChestUrl = res.url;
  834. }
  835. else{
  836. this.msgError(res.msg);
  837. }
  838. console.log(this.form.closeChestUrl)
  839. },
  840. beforeUpload(file) {
  841. const isPic =
  842. file.type === 'image/jpeg' ||
  843. file.type === 'image/png' ||
  844. file.type === 'image/gif' ||
  845. file.type === 'image/jpg'
  846. // const isLt2M = file.size / 1024 / 1024 < 2
  847. if (!isPic) {
  848. this.$message.error('上传图片只能是 JPG、JPEG、PNG、GIF 格式!')
  849. return false
  850. }
  851. return new Promise((resolve, reject) => {
  852. if (file.size / 1024 / 1024 > 3) {
  853. this.$message.error('上传的图片不能超过3MB');
  854. reject();
  855. return;
  856. }
  857. if (file.size / 1024 / 1024 > 1) {
  858. const loadingInstance = Loading.service({ text: '图片内存过大正在压缩图片...' });
  859. // 文件大于1MB时进行压缩
  860. this.compressImage(file).then((compressedFile) => {
  861. loadingInstance.close();
  862. if (compressedFile.size / 1024 > 500) {
  863. this.$message.error('图片压缩后仍大于500KB');
  864. reject();
  865. } else {
  866. // this.$message.success(`图片压缩成功,最终质量为: ${this.finalQuality.toFixed(2)}`);
  867. console.log(`图片压缩成功,最终质量为: ${this.finalQuality.toFixed(2)}`);
  868. console.log(`最终内存大小为: ${(compressedFile.size/1024).toFixed(2)}KB`);
  869. resolve(compressedFile);
  870. }
  871. }).catch((err) => {
  872. loadingInstance.close();
  873. console.error(err);
  874. reject();
  875. });
  876. } else {
  877. resolve(file);
  878. }
  879. });
  880. },
  881. compressImage(file) {
  882. return new Promise((resolve, reject) => {
  883. const reader = new FileReader();
  884. reader.readAsDataURL(file);
  885. reader.onload = (event) => {
  886. const img = new Image();
  887. img.src = event.target.result;
  888. img.onload = () => {
  889. const canvas = document.createElement('canvas');
  890. const ctx = canvas.getContext('2d');
  891. const width = img.width;
  892. const height = img.height;
  893. canvas.width = width;
  894. canvas.height = height;
  895. ctx.drawImage(img, 0, 0, width, height);
  896. let quality = 1; // 初始压缩质量
  897. let dataURL = canvas.toDataURL('image/jpeg', quality);
  898. // 逐步压缩,直到图片大小小于500KB并且压缩质量不再降低
  899. while (dataURL.length / 1024 > 500 && quality > 0.1) {
  900. quality -= 0.01;
  901. dataURL = canvas.toDataURL('image/jpeg', quality);
  902. }
  903. this.finalQuality = quality; // 存储最终的压缩质量
  904. if (dataURL.length / 1024 > 500) {
  905. reject(new Error('压缩后图片仍然大于500KB'));
  906. return;
  907. }
  908. const arr = dataURL.split(',');
  909. const mime = arr[0].match(/:(.*?);/)[1];
  910. const bstr = atob(arr[1]);
  911. let n = bstr.length;
  912. const u8arr = new Uint8Array(n);
  913. while (n--) {
  914. u8arr[n] = bstr.charCodeAt(n);
  915. }
  916. const compressedFile = new Blob([u8arr], { type: mime });
  917. compressedFile.name = file.name;
  918. resolve(compressedFile);
  919. };
  920. img.onerror = (error) => {
  921. reject(error);
  922. };
  923. };
  924. reader.onerror = (error) => {
  925. reject(error);
  926. };
  927. });
  928. },
  929. // 查看奖励详情
  930. handleViewReward(row) {
  931. this.currentReward = { ...row };
  932. this.detailVisible = true;
  933. },
  934. // 格式化JSON显示
  935. formatJson(json) {
  936. if (!json) return '无数据';
  937. try {
  938. const parsed = JSON.parse(json);
  939. return JSON.stringify(parsed, null, 2);
  940. } catch (e) {
  941. return json;
  942. }
  943. },
  944. /** 查询奖励配置列表 */
  945. getList() {
  946. this.loading = true;
  947. listReward(this.queryParams).then(response => {
  948. this.rewardList = response.rows;
  949. this.total = response.total;
  950. this.loading = false;
  951. });
  952. },
  953. // 取消按钮
  954. cancel() {
  955. this.open = false;
  956. this.reset();
  957. },
  958. // 表单重置
  959. reset() {
  960. this.form = {
  961. id: null,
  962. name: null,
  963. description: null,
  964. rewardType: null,
  965. status: "1",
  966. expectedValue: 0,
  967. account: 0,
  968. openChestUrl: "",
  969. closeChestUrl:"",
  970. rewardItems: []
  971. };
  972. this.resetForm("form");
  973. },
  974. /** 搜索按钮操作 */
  975. handleQuery() {
  976. this.queryParams.pageNum = 1;
  977. this.getList();
  978. },
  979. /** 重置按钮操作 */
  980. resetQuery() {
  981. this.resetForm("queryForm");
  982. this.handleQuery();
  983. },
  984. // 多选框选中数据
  985. handleSelectionChange(selection) {
  986. this.ids = selection.map(item => item.id)
  987. this.single = selection.length!==1
  988. this.multiple = !selection.length
  989. },
  990. /** 新增按钮操作 */
  991. handleAdd() {
  992. this.reset();
  993. this.open = true;
  994. this.title = "添加奖励配置";
  995. // 若选择转盘类型时,默认添加一条配置
  996. if (this.form.rewardType === '4') {
  997. this.ensureSpinDefaultItem();
  998. }
  999. if (this.form.rewardType === '5') {
  1000. this.ensureSpinDefaultItem2();
  1001. }
  1002. },
  1003. /** 修改按钮操作 */
  1004. handleUpdate(row) {
  1005. this.reset();
  1006. const id = row.id || this.ids
  1007. getReward(id).then(response => {
  1008. this.form = response.data;
  1009. this.form.status = response.data.status.toString();
  1010. this.form.rewardType = response.data.rewardType.toString();
  1011. // 解析actualRewards
  1012. try {
  1013. const actualRewards = JSON.parse(response.data.actualRewards || "[]");
  1014. if (this.form.rewardType === '1' || this.form.rewardType === '4' || this.form.rewardType === '5') {
  1015. // 宝箱类型
  1016. this.form.rewardItems = actualRewards.map(item => ({
  1017. ...item,
  1018. couponId: item.couponId || null,
  1019. }));
  1020. this.initCouponList(actualRewards.filter(item => item.couponId).map(item => item.couponId).join(","))
  1021. this.initGoodsList(actualRewards.filter(item => item.goodsId).map(item => item.goodsId).join(","))
  1022. } else if (this.form.rewardType === '2') {
  1023. // 红包类型
  1024. this.form.account = actualRewards.amount || 0;
  1025. } else if (this.form.rewardType === '3') {
  1026. // 芳华币类型
  1027. this.form.account = actualRewards.points || 0;
  1028. }
  1029. } catch (e) {
  1030. this.form.rewardItems = [];
  1031. this.form.account = 0;
  1032. }
  1033. this.open = true;
  1034. this.title = "修改奖励配置";
  1035. });
  1036. },
  1037. initCouponList(ids) {
  1038. if (!ids) {
  1039. return
  1040. }
  1041. getByIds({ids}).then(response => {
  1042. const {data} = response
  1043. data.forEach(d => {
  1044. if (!this.couponList.some(item => item.id === d.couponId)) {
  1045. this.couponList.push({"id": d.couponId, "name": d.title})
  1046. }
  1047. })
  1048. })
  1049. },
  1050. initGoodsList(ids) {
  1051. if (!ids) {
  1052. return
  1053. }
  1054. getGoodsByIds({ids}).then(response => {
  1055. const {data} = response
  1056. data.forEach(g => {
  1057. if (!this.goodsList.some(item => item.id === g.goodsId)) {
  1058. this.goodsList.push({"id": g.goodsId, "name": g.goodsName})
  1059. }
  1060. })
  1061. })
  1062. },
  1063. // 奖励类型变化处理
  1064. handleRewardTypeChange(value) {
  1065. // 清空相关数据
  1066. this.form.account = 0;
  1067. this.form.rewardItems = [];
  1068. this.form.expectedValue = 0
  1069. // 切换为转盘类型时,默认加入一条配置
  1070. if (value === '4') {
  1071. this.ensureSpinDefaultItem();
  1072. }
  1073. if (value === '5') {
  1074. this.ensureSpinDefaultItem2();
  1075. }
  1076. },
  1077. // 更新奖励项名称(当表单名称变化时)
  1078. updateRewardItemsName() {
  1079. if (this.form.rewardType === '1' && this.form.rewardItems.length > 0) {
  1080. // 可选:当表单名称变化时,自动更新所有奖励项的名称
  1081. // this.form.rewardItems.forEach(item => {
  1082. // item.name = this.form.name;
  1083. // });
  1084. }
  1085. },
  1086. // 添加奖励项
  1087. addRewardItem() {
  1088. this.form.rewardItems.push({
  1089. type: '1',
  1090. jltype: '1',
  1091. name: this.form.name || '', // 默认使用表单中的奖励名称
  1092. couponId: null,
  1093. amount: 1,
  1094. probability: '',
  1095. code: this.generateUUID()
  1096. });
  1097. },
  1098. // 转盘奖励:添加奖励项(包含奖励类型选择)
  1099. addSpinRewardItem() {
  1100. this.form.rewardItems.push({
  1101. type: '2',
  1102. iconUrl: '',
  1103. name: '',
  1104. couponId: null,
  1105. goodsId: null,
  1106. amount: '',
  1107. probability: '',
  1108. code: this.generateUUID()
  1109. });
  1110. },
  1111. addSpinRewardItem2() {
  1112. this.form.rewardItems.push({
  1113. type: '2',
  1114. isGuarantee: 0,
  1115. iconUrl: '',
  1116. name: '',
  1117. couponId: null,
  1118. amount: '',
  1119. probability: '',
  1120. code: this.generateUUID()
  1121. });
  1122. },
  1123. // 转盘奖励:图标上传成功
  1124. handleSpinIconSuccess(res, row) {
  1125. if (res && res.code === 200 && res.url) {
  1126. this.$set(row, 'iconUrl', res.url);
  1127. } else {
  1128. this.msgError(res && res.msg ? res.msg : '图片上传失败');
  1129. }
  1130. },
  1131. // 转盘奖励:确保有一条默认项
  1132. ensureSpinDefaultItem() {
  1133. if (!Array.isArray(this.form.rewardItems)) this.form.rewardItems = [];
  1134. if (this.form.rewardItems.length === 0) {
  1135. this.addSpinRewardItem();
  1136. }
  1137. },
  1138. ensureSpinDefaultItem2() {
  1139. if (!Array.isArray(this.form.rewardItems)) this.form.rewardItems = [];
  1140. if (this.form.rewardItems.length === 0) {
  1141. this.addSpinRewardItem2();
  1142. }
  1143. },
  1144. // 移除奖励项
  1145. removeRewardItem(index) {
  1146. this.form.rewardItems.splice(index, 1);
  1147. },
  1148. // 生成UUID
  1149. generateUUID(item) {
  1150. if (item) {
  1151. item.code = this.generateUUIDString();
  1152. } else {
  1153. return this.generateUUIDString();
  1154. }
  1155. },
  1156. // 生成UUID字符串
  1157. generateUUIDString() {
  1158. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  1159. var r = Math.random() * 16 | 0,
  1160. v = c == 'x' ? r : (r & 0x3 | 0x8);
  1161. return v.toString(16);
  1162. });
  1163. },
  1164. // 获取转盘奖励类型标签
  1165. getSpinItemTypeLabel(type) {
  1166. const option = this.spinItemTypeOptions.find(item => item.dictValue === type.toString());
  1167. return option ? option.dictLabel : type;
  1168. },
  1169. /** 提交按钮 */
  1170. submitForm() {
  1171. this.$refs["form"].validate(valid => {
  1172. if (valid) {
  1173. let actualRewards = "";
  1174. if (this.form.rewardType === '1' || this.form.rewardType === '4' || this.form.rewardType === '5') {
  1175. // 宝箱类型,将rewardItems转为JSON
  1176. actualRewards = JSON.stringify(this.form.rewardItems);
  1177. } else if (this.form.rewardType === '2') {
  1178. // 红包类型
  1179. actualRewards = JSON.stringify({ amount: this.form.account });
  1180. } else if (this.form.rewardType === '3') {
  1181. // 芳华币类型
  1182. actualRewards = JSON.stringify({ points: this.form.account });
  1183. }
  1184. // 设置actualRewards
  1185. this.form.actualRewards = actualRewards;
  1186. if (this.form.id != null) {
  1187. updateReward(this.form).then(response => {
  1188. this.msgSuccess("修改成功");
  1189. this.open = false;
  1190. this.getList();
  1191. });
  1192. } else {
  1193. addReward(this.form).then(response => {
  1194. this.msgSuccess("新增成功");
  1195. this.open = false;
  1196. this.getList();
  1197. });
  1198. }
  1199. }
  1200. });
  1201. },
  1202. /** 删除按钮操作 */
  1203. handleDelete(row) {
  1204. const ids = row.id || this.ids;
  1205. this.$confirm('是否确认删除奖励配置编号为"' + ids + '"的数据项?', "警告", {
  1206. confirmButtonText: "确定",
  1207. cancelButtonText: "取消",
  1208. type: "warning"
  1209. }).then(function() {
  1210. return delReward(ids);
  1211. }).then(() => {
  1212. this.getList();
  1213. this.msgSuccess("删除成功");
  1214. }).catch(() => {});
  1215. },
  1216. /** 导出按钮操作 */
  1217. handleExport() {
  1218. const queryParams = this.queryParams;
  1219. this.$confirm('是否确认导出所有奖励配置数据项?', "警告", {
  1220. confirmButtonText: "确定",
  1221. cancelButtonText: "取消",
  1222. type: "warning"
  1223. }).then(() => {
  1224. this.exportLoading = true;
  1225. return exportReward(queryParams);
  1226. }).then(response => {
  1227. this.download(response.msg);
  1228. this.exportLoading = false;
  1229. }).catch(() => {});
  1230. }
  1231. }
  1232. };
  1233. </script>
  1234. <style scoped>
  1235. .spin-reward .el-table th,
  1236. .spin-reward .el-table td {
  1237. padding: 6px 8px;
  1238. }
  1239. .spin-reward .probability-col .el-input__inner {
  1240. text-align: center;
  1241. }
  1242. .spin-tips {
  1243. padding: 8px 12px;
  1244. color: #909399;
  1245. font-size: 12px;
  1246. background-color: #fafafa;
  1247. border-top: 1px solid #ebeef5;
  1248. border-bottom: 1px solid #ebeef5;
  1249. }
  1250. .avatar-uploader .el-upload {
  1251. border: 1px dashed #d9d9d9;
  1252. border-radius: 6px;
  1253. cursor: pointer;
  1254. position: relative;
  1255. overflow: hidden;
  1256. }
  1257. .avatar-uploader .el-upload:hover {
  1258. border-color: #409EFF;
  1259. }
  1260. .avatar-uploader-icon {
  1261. font-size: 28px;
  1262. color: #8c939d;
  1263. width: 178px;
  1264. height: 178px;
  1265. line-height: 178px;
  1266. text-align: center;
  1267. }
  1268. .avatar {
  1269. width: 120px;
  1270. height: 120px;
  1271. display: block;
  1272. }
  1273. .spin-icon-uploader {
  1274. display: inline-block;
  1275. }
  1276. .spin-icon {
  1277. width: 32px;
  1278. height: 32px;
  1279. object-fit: cover;
  1280. border: 1px solid #ebeef5;
  1281. border-radius: 4px;
  1282. }
  1283. .spin-icon-uploader .el-upload {
  1284. border: 1px dashed #d9d9d9;
  1285. border-radius: 4px;
  1286. width: 32px;
  1287. height: 32px;
  1288. line-height: 32px;
  1289. display: inline-flex;
  1290. align-items: center;
  1291. justify-content: center;
  1292. }
  1293. .spin-icon-uploader .el-upload:hover {
  1294. border-color: #409EFF;
  1295. }
  1296. /* 覆盖全局 .avatar-uploader-icon 的大尺寸,限定在转盘上传器内为 32x32 */
  1297. .spin-icon-uploader .avatar-uploader-icon {
  1298. width: 32px !important;
  1299. height: 32px !important;
  1300. line-height: 32px !important;
  1301. font-size: 16px !important;
  1302. color: #8c939d;
  1303. display: inline-flex;
  1304. align-items: center;
  1305. justify-content: center;
  1306. }
  1307. .reward-table-container {
  1308. border: 1px solid #ebeef5;
  1309. border-radius: 4px;
  1310. }
  1311. .table-header {
  1312. display: flex;
  1313. justify-content: space-between;
  1314. align-items: center;
  1315. padding: 15px;
  1316. background-color: #f5f7fa;
  1317. border-bottom: 1px solid #ebeef5;
  1318. }
  1319. .table-tips {
  1320. padding: 10px 15px;
  1321. color: #909399;
  1322. font-size: 12px;
  1323. background-color: #f5f7fa;
  1324. }
  1325. .reward-detail-container {
  1326. max-height: 60vh;
  1327. overflow-y: auto;
  1328. }
  1329. .base-info {
  1330. margin-bottom: 20px;
  1331. width: 100%;
  1332. table-layout: fixed;
  1333. }
  1334. .base-info .el-descriptions__label {
  1335. width: 120px !important;
  1336. min-width: 120px;
  1337. }
  1338. .base-info .el-descriptions__content {
  1339. width: 200px !important;
  1340. min-width: 200px;
  1341. }
  1342. .reward-content {
  1343. margin: 20px 0;
  1344. }
  1345. .reward-content h4 {
  1346. margin-bottom: 15px;
  1347. color: #303133;
  1348. font-weight: 600;
  1349. }
  1350. .chest-reward,
  1351. .redpacket-reward,
  1352. .points-reward {
  1353. margin: 15px 0;
  1354. }
  1355. .reward-amount {
  1356. display: flex;
  1357. align-items: center;
  1358. gap: 10px;
  1359. padding: 15px;
  1360. background: #f8f9fa;
  1361. border-radius: 4px;
  1362. }
  1363. .amount-text {
  1364. font-size: 18px;
  1365. font-weight: bold;
  1366. color: #409EFF;
  1367. }
  1368. .code-text {
  1369. font-family: monospace;
  1370. font-size: 12px;
  1371. }
  1372. .empty-tip {
  1373. text-align: center;
  1374. color: #909399;
  1375. padding: 20px;
  1376. }
  1377. .json-pre {
  1378. background: #f5f7fa;
  1379. padding: 12px;
  1380. border-radius: 4px;
  1381. font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
  1382. font-size: 12px;
  1383. line-height: 1.5;
  1384. overflow: auto;
  1385. max-height: 200px;
  1386. margin: 0;
  1387. }
  1388. .raw-data {
  1389. margin-top: 20px;
  1390. }
  1391. /* 新增样式 */
  1392. .image-upload-group {
  1393. display: flex;
  1394. flex-wrap: wrap;
  1395. gap: 20px;
  1396. }
  1397. .image-upload-item {
  1398. display: flex;
  1399. flex-direction: column;
  1400. gap: 10px;
  1401. }
  1402. .image-preview {
  1403. margin-top: 10px;
  1404. border: 1px dashed #d9d9d9;
  1405. padding: 10px;
  1406. border-radius: 4px;
  1407. }
  1408. .image-actions {
  1409. margin-top: 10px;
  1410. display: flex;
  1411. gap: 10px;
  1412. }
  1413. .chest-images-preview {
  1414. display: flex;
  1415. justify-content: center;
  1416. align-items: center;
  1417. }
  1418. .chest-images-section {
  1419. margin: 20px 0;
  1420. }
  1421. .chest-images-display {
  1422. display: flex;
  1423. gap: 30px;
  1424. justify-content: center;
  1425. margin-top: 15px;
  1426. }
  1427. .chest-image-item {
  1428. text-align: center;
  1429. }
  1430. .chest-image-item p {
  1431. margin-bottom: 8px;
  1432. font-weight: bold;
  1433. }
  1434. </style>