You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1708 lines
68 KiB

5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
  1. <template>
  2. <div class="content">
  3. <div class="div-card1">
  4. <el-card class="card1" style="margin-bottom: 0.5vh; min-height: 110px;">
  5. <div class="row1">
  6. <div class="rowItem">
  7. <el-text style="width: 4vw;">{{ t('common.jwcode') }}</el-text>
  8. <el-input v-model="searchData.jwcode" :placeholder="t('common.jwcodePlaceholder')"
  9. style="width:10vw;" clearable></el-input>
  10. </div>
  11. <div class="rowItem">
  12. <el-text style="width: 4vw;">{{ t('common.customerName') }}</el-text>
  13. <el-input v-model="searchData.name" :placeholder="t('common.customerNamePlaceholder')"
  14. style="width:10vw;" clearable></el-input>
  15. </div>
  16. <div class="rowItem">
  17. <el-text style="width: 4vw;">{{ t('common.market') }}</el-text>
  18. <!-- <el-select v-model="searchData.market" placeholder="请选择所属地区" style="width:10vw;" clearable>
  19. <el-option v-for="item in market" :key="item" :label="item" :value="item" />
  20. </el-select> -->
  21. <el-cascader style="width: 9vw;" v-model="searchData.markets" :options="market"
  22. :placeholder="t('common.marketPlaceholder')" clearable @change="handleMarketChange" />
  23. </div>
  24. <!-- 地区财务固定显示付款币种删除客服的订单状态 -->
  25. <div class="rowItem">
  26. <el-text style="width: 4vw;">{{ t('common.payCurrency') }}</el-text>
  27. <el-select v-model="searchData.paymentCurrency"
  28. :placeholder="t('common.payCurrencyPlaceholder')" style="width: 10vw;" clearable>
  29. <el-option v-for="item in customOptions" :key="item" :label="item" :value="item" />
  30. </el-select>
  31. </div>
  32. <div class="rowItem">
  33. <el-text style="width: 4vw;">{{ t('common.payModel') }}</el-text>
  34. <el-select v-model="searchData.payType" :placeholder="t('common.payModelPlaceholder')"
  35. style="width: 10vw;" clearable>
  36. <el-option v-for="item in paytypeList" :key="item" :label="item" :value="item" />
  37. </el-select>
  38. </div>
  39. </div>
  40. <div class="row2" style="margin-top: 10px;">
  41. <div class="left">
  42. <div class="rowItem">
  43. <el-text style="width: 4vw;">{{ t('common.activityName') }}</el-text>
  44. <el-select v-model="searchData.activity" :placeholder="t('common.activityNameChoose')"
  45. style="width: 10vw;" clearable>
  46. <el-option v-for="item in activityList" :key="item.id" :label="item.activityName"
  47. :value="item.id" />
  48. </el-select>
  49. </div>
  50. <div class="rowItem">
  51. <el-text style="width: 4vw;">{{ t('common.productName') }}</el-text>
  52. <el-cascader v-model="searchData.goodsName" :options="productList" style="width: 10vw;"
  53. clearable />
  54. </div>
  55. <div class="rowItem" style="width: 25vw">
  56. <el-text style="width: 4vw; margin-left: 0.5vw;">{{ t('common.payTime') }}</el-text>
  57. <el-date-picker v-model="getTime" type="datetimerange" :range-separator="t('common.to')"
  58. :start-placeholder="t('common.startTime')" :end-placeholder="t('common.endTime')"
  59. style="width: 22vw; " @change="handleDatePickerChange" :default-time="defaultTime"
  60. :disabled-date="disabledDate" />
  61. </div>
  62. </div>
  63. <el-button style="width: 3vw;" type="primary" @click="search">{{ t('common.search') }}</el-button>
  64. <el-button style="width: 3vw;" type="success" @click="reset">{{ t('common.reset') }}</el-button>
  65. <el-button v-if="activeTab == 'done'" style="width: 5vw;" type="warning" @click="exportExcel()">{{
  66. t('common.exportExcel') }}</el-button>
  67. <el-button v-if="activeTab == 'done'" style="width: 6vw;" type="primary" @click="openExportList">{{
  68. t('common.viewExportList') }}</el-button>
  69. </div>
  70. </el-card>
  71. </div>
  72. <div class="div-card2">
  73. <el-card class="card2">
  74. <div class="btns">
  75. <div class="tabs">
  76. <el-button-group>
  77. <el-button class="btnItem"
  78. :style="{ backgroundColor: activeTab === 'pass' ? '#2741DE' : '#E5EBFE', color: activeTab === 'pass' ? 'white' : '#666' }"
  79. @click="navigateTo('pass')">
  80. {{ t('common.passed') }}
  81. </el-button>
  82. <el-button class="btnItem"
  83. :style="{ backgroundColor: activeTab === 'done' ? '#2741DE' : '#E5EBFE', color: activeTab === 'done' ? 'white' : '#666' }"
  84. @click="navigateTo('done')">
  85. {{ t('common.completed') }}
  86. </el-button>
  87. </el-button-group>
  88. </div>
  89. <div class="info-tooltip">
  90. <el-popover placement="top" :title="t('cash.receiveCashDataTitle')" :width="260" trigger="hover"
  91. :content="t('cash.receiveCashDataContent')" popper-class="custom-popover"
  92. :show-arrow="false">
  93. <template #reference>
  94. <el-icon class="service-icon">
  95. <Warning />
  96. </el-icon>
  97. </template>
  98. </el-popover>
  99. </div>
  100. </div>
  101. <div class="table">
  102. <el-table ref="tableRef" :data="tableData" style="width: 80vw;height:64vh;" @sort-change="handleSortChange"
  103. :row-style="{ height: '60px' }" :header-cell-style="{ textAlign: 'center' }"
  104. :cell-style="{ textAlign: 'center' }">
  105. <el-table-column type="index" :label="t('common_list.id')" width="100px" fixed="left">
  106. <template #default="scope">
  107. <span>{{ scope.$index + 1 + (pageInfo.pageNum - 1) * pageInfo.pageSize }}</span>
  108. </template>
  109. </el-table-column>
  110. <el-table-column fixed="left" prop="jwcode" label="Homily ID" width="110px" />
  111. <el-table-column fixed="left" prop="name" :label="t('common_list.name')" width="110px" />
  112. <el-table-column prop="marketName" :label="t('common_list.market')" width="80px" />
  113. <el-table-column prop="activity" :label="t('common_list.activity')" width="120px"
  114. show-overflow-tooltip />
  115. <el-table-column prop="goodsName" :label="t('common_list.productName')" width="120px" />
  116. <el-table-column prop="goodNum" :label="t('common_list.productNum')" width="130px">
  117. <template #default="scope">
  118. <span v-if="scope.row.goodsName == t('cash.coinRecharge')">{{ scope.row.permanentGold
  119. }}</span>
  120. <span v-else>{{ scope.row.goodNum }}</span>
  121. </template>
  122. </el-table-column>
  123. <el-table-column prop="paymentCurrency" :label="t('common_list.payCurrency')" width="100px" />
  124. <el-table-column prop="paymentAmount" :label="t('common_list.payAmount')" width="120px" />
  125. <!-- 总部财务显示到账币种/金额/手续费已通过/已完成标签页 -->
  126. <el-table-column prop="receivedCurrency" :label="t('common_list.receiveCurrency')"
  127. v-if="activeTab == 'pass' || activeTab == 'done'" width="150px"></el-table-column>
  128. <el-table-column prop="receivedAmount" :label="t('common_list.receiveAmount')"
  129. v-if="activeTab == 'pass' || activeTab == 'done'" width="150px">
  130. <template #default="scope">
  131. <div v-if="!scope.row.receivedAmount">
  132. <text style="color: #FA5A1E;">{{ t('common_list.toSupply') }}</text>
  133. </div>
  134. </template>
  135. </el-table-column>
  136. <el-table-column prop="handlingCharge" :label="t('common_list.fee')"
  137. v-if="activeTab == 'pass' || activeTab == 'done'" width="150px">
  138. <template #default="scope">
  139. <div v-if="scope.row.handlingCharge == null">
  140. <text style="color: #FA5A1E;">{{ t('common_list.toSupply') }}</text>
  141. </div>
  142. </template>
  143. </el-table-column>
  144. <el-table-column prop="payType" :label="t('common_list.payModel')" width="130px" />
  145. <el-table-column prop="payTime" :label="t('common_list.payTime')" width="180px" />
  146. <el-table-column prop="voucher" :label="t('common_list.transferVoucher')" width="110px">
  147. <template #default="scope">
  148. <div v-if="scope.row.voucher"
  149. style="display: flex; justify-content: center; align-items: center; cursor: pointer;"
  150. @click="previewImage(scope.row.voucher)">
  151. <img :src="scope.row.voucher" :alt="t('common_list.payVoucher')"
  152. style="width: auto; height: 40px;">
  153. </div>
  154. <div v-else
  155. style="display: flex; justify-content: center; align-items: center; height: 40px;">
  156. </div>
  157. </template>
  158. </el-table-column>
  159. <el-table-column prop="submitterName" :label="t('common_list.submitter')" width="150px"
  160. show-overflow-tooltip></el-table-column>
  161. <!-- 总部财务显示审核人已通过/已驳回/已完成标签页 -->
  162. <el-table-column prop="auditName" :label="t('common_list.approver')"
  163. v-if="activeTab == 'pass' || activeTab == 'reject' || activeTab == 'done'" width="150px"
  164. show-overflow-tooltip></el-table-column>
  165. <el-table-column prop="receivedTime" :label="t('common_list.receiveTime')"
  166. v-if="activeTab == 'pass' || activeTab == 'done'" width="180px" />
  167. <el-table-column prop="remark" :label="t('common_list.remark')" v-if="activeTab != 'reject'"
  168. width="150px" show-overflow-tooltip></el-table-column>
  169. <el-table-column prop="status" fixed="right" :label="t('common_list.orderStatus')"
  170. v-if="activeTab == 'done'" width="150px" show-overflow-tooltip>
  171. <template #default="scope">
  172. <span style="color: rgb(242, 84, 83);" v-if="scope.row.status == 6">{{
  173. t('common_list.refund') }}</span>
  174. <span style="color: rgb(127,204,133);" v-else>{{ t('common_list.normal') }}</span>
  175. </template>
  176. </el-table-column>
  177. <el-table-column prop="auditTime" :label="t('common_list.rejectTime')"
  178. v-if="activeTab == 'reject'" width="180px" show-overflow-tooltip></el-table-column>
  179. <el-table-column prop="rejectReason" :label="t('common_list.rejectReason')"
  180. v-if="activeTab == 'reject'" width="150px">
  181. <template #default="scope">
  182. <div class="ellipsis-container"
  183. @mouseenter="handleMouseEnter($event, scope.row.rejectReason)"
  184. @mouseleave="handleMouseLeave" @mousemove="handleMouseMove($event)">
  185. <span class="ellipsis-text">
  186. {{ scope.row.rejectReason || '—' }}
  187. </span>
  188. <!-- 自定义提示框 -->
  189. <div v-if="showTooltip && tooltipContent" class="custom-tooltip" :style="{
  190. left: `${tooltipLeft}px`,
  191. top: `${tooltipTop}px`
  192. }">
  193. {{ tooltipContent }}
  194. </div>
  195. </div>
  196. </template>
  197. </el-table-column>
  198. <!-- 总部财务表格操作待审核审核已通过编辑 -->
  199. <el-table-column fixed="right" :label="t('common_list.operation')" width="120px"
  200. v-if="activeTab != 'reject'">
  201. <template #default=scope>
  202. <el-link v-if="activeTab == 'wait'" style="color: #2741DE;"
  203. @click="openAuditForm(scope.row)">{{ t('common.audit') }}</el-link>
  204. <el-link
  205. v-else-if="activeTab == 'pass' && !(scope.row.status == 6 || scope.row.status == 4)"
  206. style="color: #2741DE;" @click="openEditForm(scope.row)">{{ t('common.edit')
  207. }}</el-link>
  208. <el-link
  209. v-else-if="activeTab == 'done' && scope.row.status == 4 && startsWith(scope.row.orderCode, 'GOLDCOIN')"
  210. style="color: rgb(242, 84, 83);" @click="openRefundConfirm(scope.row)">{{
  211. t('common.refund') }}</el-link>
  212. </template>
  213. </el-table-column>
  214. </el-table>
  215. </div>
  216. <div class="pagination">
  217. <el-pagination background :current-page="pageInfo.pageNum" :page-size="pageInfo.pageSize"
  218. :page-sizes="[5, 10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper"
  219. :total="total" @size-change="handlePagination('size', $event)"
  220. @current-change="handlePagination('page', $event)"></el-pagination>
  221. </div>
  222. </el-card>
  223. </div>
  224. <!-- 退款确认弹窗 -->
  225. <div class="recallDialog" v-show="refundConfirmDialog">
  226. <div class="close">
  227. <button @click="closeConfirmRefund" class="Btn">{{ t('common.close') }}</button>
  228. </div>
  229. <div class="text">
  230. <text class="txt">{{ textContent }}</text>
  231. </div>
  232. <div class="cancle">
  233. <button @click="closeConfirmRefund" class="Btn">{{ t('common.cancel') }}</button>
  234. </div>
  235. <div class="confirm">
  236. <button @click="openRefundDialog" class="Btn">{{ t('common.confirm') }}</button>
  237. </div>
  238. </div>
  239. <!-- 编辑填手续费弹窗总部财务核心编辑功能 -->
  240. <el-dialog class="editdialog" v-model="editFormisible" width="28vw" :before-close="closeEditForm">
  241. <div class="content">
  242. <div class="left">
  243. <el-form class="editForm" label-width="6vw" label-position="left">
  244. <el-form-item :label="t('common_add.jwcode')">
  245. <el-input disabled="true" v-model="editFormData.jwcode"
  246. :placeholder="t('common_add.jwcode')" />
  247. </el-form-item>
  248. <el-form-item :label="t('common_add.customerName')">
  249. <el-input disabled="true" v-model="editFormData.name"
  250. :placeholder="t('common_add.customerName')" />
  251. </el-form-item>
  252. <el-form-item :label="t('common_add.market')">
  253. <el-input disabled="true" v-model="editFormData.marketName"
  254. :placeholder="t('common_add.market')" />
  255. </el-form-item>
  256. <el-form-item :label="t('common_add.activity')">
  257. <el-input disabled="true" v-model="editFormData.activity"
  258. :placeholder="t('common_add.activity')" />
  259. </el-form-item>
  260. <el-form-item :label="t('common_add.productName')">
  261. <el-select disabled="true" v-model="editFormData.goodsName"
  262. :placeholder="t('common_add.productName')" clearable></el-select>
  263. </el-form-item>
  264. <!-- 金币产品特殊显示 -->
  265. <el-form-item v-show="!isEditGold" :label="t('common_add.productNum')">
  266. <div style="display: flex;">
  267. <el-input disabled="true" style="padding-right: 30px; flex: 3;"
  268. v-model="editFormData.goodNum"
  269. :placeholder="t('common_add.productNumPlaceholder')" />
  270. <CurrencySelect disabled="true" v-model="editFormData.numUnit" :items="numUnitList"
  271. style="flex: 1.5;" :placeholder="t('common_add.numUnit')" />
  272. </div>
  273. </el-form-item>
  274. <div v-show="isEditGold" style="margin-bottom: 15px; display: flex;">
  275. <div style=" display: flex; margin-right: 10px;">
  276. <span style="color: #999999; display: flex; white-space: nowrap;align-items: center;">{{
  277. t('common_add.permanentGold') }}</span>
  278. <el-input disabled="true"
  279. style="padding-right: 10px; padding-left: 10px; height: 30px; width: 110px;"
  280. v-model="editFormData.permanentGold" />
  281. </div>
  282. <div style="padding-right: 5px; display: flex;">
  283. <span style="color: #999999; display: flex; white-space: nowrap;align-items: center;">{{
  284. t('common_add.freeGold') }}</span>
  285. <el-input disabled="true"
  286. style="padding-right: 10px; padding-left: 10px; height: 30px; width: 110px;"
  287. v-model="editFormData.freeGold" />
  288. </div>
  289. </div>
  290. <el-form-item :label="t('common_add.payCurrency')">
  291. <CurrencySelect :disabled="!ifOnline" v-model="editFormData.paymentCurrency"
  292. :items="customOptions" :placeholder="t('common_add.payCurrencyPlaceholder')" />
  293. </el-form-item>
  294. <el-form-item :label="t('common_add.payAmount')">
  295. <el-input :disabled="!ifOnline" v-model="editFormData.paymentAmount"
  296. :placeholder="t('common_add.payAmountPlaceholder')" />
  297. </el-form-item>
  298. <el-form-item :label="t('common_add.payMethod')">
  299. <el-select disabled="true" v-model="editFormData.payType"
  300. :placeholder="t('common_add.payMethodPlaceholder')" clearable></el-select>
  301. </el-form-item>
  302. <el-form-item :label="t('common_add.receiveArea')">
  303. <el-select disabled="true" v-model="editFormData.receivedMarket"
  304. :placeholder="t('common_add.receiveAreaPlaceholder')" clearable></el-select>
  305. </el-form-item>
  306. <el-form-item :label="t('common_add.payTime')">
  307. <el-date-picker disabled="true" type="datetime" v-model="editFormData.payTime"
  308. :placeholder="t('common_add.payTimePlaceholder')" />
  309. </el-form-item>
  310. <el-form-item :label="t('common_add.transferVoucher')">
  311. <div class="pic">
  312. <el-upload disabled="true" ref="uploadRef" class="uploader" :show-file-list="false"
  313. list-type="picture-card" :auto-upload="false" :before-upload="beforeUpload"
  314. :on-error="handelImgErr" :on-change="handleImageChange"
  315. :http-request="customUpload">
  316. <img v-if="editFormData.voucher" :src="editFormData.voucher" class="avatar"
  317. style="height: 100%; width: 100%; object-fit: cover;" />
  318. <el-icon v-else class="avatar-uploader-icon">
  319. <Plus />
  320. </el-icon>
  321. </el-upload>
  322. <el-text class="picText">{{ t('common_add.transferVoucherPlaceholder') }}</el-text>
  323. </div>
  324. </el-form-item>
  325. <el-form-item :label="t('common_add.remark')">
  326. <el-input disabled="true" v-model="editFormData.remark" type="textarea" :rows="4"
  327. :placeholder="t('common_add.remark')" maxlength="100" show-word-limit />
  328. </el-form-item>
  329. </el-form>
  330. </div>
  331. <div class="right">
  332. <!-- 总部财务可编辑项仅到账相关信息 -->
  333. <el-form ref="editFormRef" :rules="editFormRule" :model="editFormData" class="editFormRighrt"
  334. label-width="6vw" label-position="left">
  335. <el-form-item :label="t('common_add.receiveCurrency')" required>
  336. <CurrencySelect v-model="editFormData.receivedCurrency" :items="customOptions"
  337. :placeholder="t('common_add.receiveCurrencyPlaceholder')" />
  338. </el-form-item>
  339. <el-form-item :label="t('common_add.receiveAmount')" prop="receivedAmount">
  340. <el-input v-model="editFormData.receivedAmount"
  341. :placeholder="t('common_add.receiveAmountPlaceholder')" type="number" />
  342. </el-form-item>
  343. <el-form-item :label="t('common_add.fee')" prop="handlingCharge">
  344. <el-input v-model="editFormData.handlingCharge"
  345. :placeholder="t('common_add.feePlaceholder')" type="number" />
  346. </el-form-item>
  347. <el-form-item :label="t('common_add.receiveTime')" required>
  348. <el-date-picker type="datetime" v-model="editFormData.receivedTime"
  349. :placeholder="t('common_add.receiveTimePlaceholder')" />
  350. </el-form-item>
  351. </el-form>
  352. <span class="editBtns">
  353. <button class="editBtn1" @click="closeEditForm">
  354. <text class="txt">{{ t('common.cancel') }}</text>
  355. </button>
  356. <button class="editBtn2" @click="throttledsubmitEditForm">
  357. <text class="txt">{{ t('common.submit') }}</text>
  358. </button>
  359. </span>
  360. </div>
  361. </div>
  362. </el-dialog>
  363. <!-- 新增退款 -->
  364. <el-dialog v-model="refundDialog" :title="t('common_add.refund')" class="refundDialog" overflow draggable
  365. style="width: 40vw;" :before-close="closeRefundForm">
  366. <div style="display: flex;">
  367. <div class="left">
  368. <div class="add-item">
  369. <el-text style="width:4vw;">{{ t('common_add.jwcode') }}</el-text>
  370. <el-input v-model="refundFormData.jwcode" style="width:10vw;" disabled />
  371. </div>
  372. <div class="add-item">
  373. <el-text style="width:4vw;">{{ t('common_add.customerName') }}</el-text>
  374. <el-input v-model="refundFormData.name" style="width:10vw;" disabled />
  375. </div>
  376. <div class="add-item">
  377. <el-text style="width:4vw;">{{ t('common_add.market') }}</el-text>
  378. <el-input v-model="refundFormData.marketName" style="width:10vw;" disabled />
  379. </div>
  380. <div class="add-item">
  381. <el-text style="width:4vw;">{{ t('common_add.activity') }}</el-text>
  382. <el-input v-model="refundFormData.activity" style="width:10vw;" disabled />
  383. </div>
  384. <div class="add-item">
  385. <el-text style="width:4vw;">{{ t('common_add.productName') }}</el-text>
  386. <el-input v-model="refundFormData.goodsName" style="width:10vw;" disabled />
  387. </div>
  388. <div v-show="!isRefundGold" class="add-item">
  389. <el-text style="width:4vw;">{{ t('common_add.productNum') }}</el-text>
  390. <el-input style="padding-right: 10px; width:6.5vw;" v-model="refundFormData.goodNum"
  391. :placeholder="t('common_add.productNumPlaceholder')" disabled />
  392. <CurrencySelect disabled="true" v-model="refundFormData.numUnit" :items="numUnitList"
  393. style=" width: 3.5vw;" :placeholder="t('common_add.numUnit')" />
  394. </div>
  395. <div v-show="isRefundGold" style="display: flex; margin-bottom: 10px;">
  396. <div style=" display: flex; align-items: center;justify-content: center; ">
  397. <span style="color: #999999; white-space: nowrap;">{{ t('common_add.permanentGold')
  398. }}</span>
  399. <el-input style="padding-right: 10px; height: 30px; width: 70px;"
  400. v-model="refundFormData.permanentGold" disabled />
  401. </div>
  402. <div style=" display: flex; align-items: center;justify-content: center; ">
  403. <span style="color: #999999; white-space: nowrap;">{{ t('common_add.freeGold') }}</span>
  404. <el-input style="padding-right: 10px; height: 30px; width: 70px;"
  405. v-model="refundFormData.freeGold" disabled />
  406. </div>
  407. </div>
  408. <div class="add-item">
  409. <el-text style="width:4vw;">{{ t('common_add.payCurrency') }}</el-text>
  410. <el-input v-model="refundFormData.paymentCurrency" style="width:10vw;" disabled />
  411. </div>
  412. <div class="add-item">
  413. <el-text style="width:4vw;">{{ t('common_add.payAmount') }}</el-text>
  414. <el-input v-model="refundFormData.paymentAmount" style="width:10vw;" disabled />
  415. </div>
  416. <div class="add-item">
  417. <el-text style="width:4vw;">{{ t('common_add.payMethod') }}</el-text>
  418. <el-input v-model="refundFormData.payType" style="width:10vw;" disabled />
  419. </div>
  420. <div class="add-item">
  421. <el-text style="width:4vw;">{{ t('common_add.payTime') }}</el-text>
  422. <el-date-picker v-model="refundFormData.payTime" type="datetime" style="width:10vw;" disabled />
  423. </div>
  424. <div class="add-item">
  425. <el-text style="width:4vw;" size="small">{{ t('common_add.transferVoucher') }}</el-text>
  426. <el-form-item
  427. :rules="{ required: true, message: t('common_add.uploadPhoto'), trigger: 'change' }">
  428. <el-upload ref="uploadRef" :auto-upload="false" list-type="picture-card"
  429. :show-file-list="false">
  430. <template #default>
  431. <img v-if="refundFormData.voucher" :src="refundFormData.voucher"
  432. style="width: 100%; height: 100%; object-fit: cover;">
  433. <el-icon v-else>
  434. <Plus />
  435. </el-icon>
  436. </template>
  437. </el-upload>
  438. </el-form-item>
  439. </div>
  440. <div class="add-item">
  441. <el-text style="width:4vw;">{{ t('common_add.remark') }}</el-text>
  442. <el-input v-model="refundFormData.remark" style="width:10vw;" :rows="2" type="textarea"
  443. maxLength="100" disabled show-word-limit />
  444. </div>
  445. </div>
  446. <div class="right">
  447. <div class="add-item">
  448. <el-text style="width:4vw;">{{ t('common_add.refundModel') }}</el-text>
  449. <el-radio-group v-model="refundFormData.refundModel">
  450. <el-radio value="0">{{ t('common_add.refundModelAll') }}</el-radio>
  451. <el-radio value="1">{{ t('common_add.refundModelPart') }}</el-radio>
  452. </el-radio-group>
  453. </div>
  454. <div v-show="refundFormData.refundModel == '1'" style="display: flex; margin-bottom: 10px;">
  455. <div style=" display: flex; align-items: center;justify-content: center; ">
  456. <span style="color: #999999; white-space: nowrap;">{{ t('common_add.permanentGold')
  457. }}</span>
  458. <el-input style="padding-right: 10px; height: 30px; width: 70px;"
  459. v-model="refundFormData.partRefundGold" disabled />
  460. </div>
  461. <div style=" display: flex; align-items: center;justify-content: center; ">
  462. <span style="color: #999999; white-space: nowrap;">{{ t('common_add.freeGold') }}</span>
  463. <el-input style="padding-right: 10px; height: 30px; width: 70px;"
  464. v-model="refundFormData.partRefundFree" />
  465. </div>
  466. </div>
  467. <div class="add-item">
  468. <el-text style="width:4vw;">{{ t('common_add.refundReason') }}</el-text>
  469. <el-input v-model="refundFormData.refundReason" style="width:10vw;" :rows="5" maxlength="150"
  470. show-word-limit type="textarea" />
  471. </div>
  472. <div>{{ t('common_add.tip') }}</div>
  473. <div style="display:flex;justify-content: center;margin-top: 5vh;">
  474. <el-button type="default" @click="resetRefund">{{ t('common.reset') }}</el-button>
  475. <el-button type="primary" @click="throttledsubmitRefund">{{ t('common.submit') }}</el-button>
  476. </div>
  477. </div>
  478. </div>
  479. </el-dialog>
  480. <!-- 导出列表 -->
  481. <el-dialog v-model="exportListVisible" :title="t('common_export.exportList')" width="60vw">
  482. <el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
  483. <el-table-column prop="fileName" :label="t('common_export.fileName')" />
  484. <el-table-column prop="state" :label="t('common_export.status')">
  485. <template #default="scope">
  486. <el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
  487. {{ getTagText(scope.row.state) }}
  488. </el-tag>
  489. </template>
  490. </el-table-column>
  491. <el-table-column prop="createTime" :label="t('common_export.createTime')">
  492. <template #default="scope">
  493. {{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  494. </template>
  495. </el-table-column>
  496. <el-table-column :label="t('common_export.operation')">
  497. <template #default="scope">
  498. <el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
  499. :disabled="scope.row.state !== 2">
  500. {{ t('common_export.download') }}
  501. </el-button>
  502. </template>
  503. </el-table-column>
  504. </el-table>
  505. <template #footer>
  506. <div class="dialog-footer">
  507. <el-button text @click="exportListVisible = false">{{ t('common_export.close') }}</el-button>
  508. </div>
  509. </template>
  510. </el-dialog>
  511. </div>
  512. </template>
  513. <script setup>
  514. // 仅导入总部财务所需依赖
  515. import { ref, watch, onMounted, nextTick, onBeforeUnmount } from 'vue';
  516. import { storeToRefs } from 'pinia';
  517. import { ElMessage, ElMessageBox } from 'element-plus';
  518. import API from '@/util/http.js';
  519. import request from '@/util/http.js';
  520. import moment from 'moment';
  521. import _ from 'lodash';
  522. import { Plus } from '@element-plus/icons-vue';
  523. import { startsWith } from './utils/util.js'
  524. import { isNumber } from 'lodash'
  525. // 筛选地区树
  526. const market = ref([])
  527. // 总部财务专属组件
  528. import CurrencySelect from '@/components/MoneyManage/CurrencySelect.vue';
  529. // 静态数据与规则(仅保留必要项)
  530. import { editFormRule } from './utils/recriveFormRules.js';
  531. import { productList, MarketNameForId, CurrencyForId, marketList, normalizeSubmitterMarket } from './utils/staticData.js';
  532. import { useAdminStore } from '@/store/index.js';
  533. import { hasMenuPermission } from '@/utils/menuTreePermission.js';
  534. // 国际化
  535. import { useI18n } from 'vue-i18n'
  536. const { t } = useI18n()
  537. // ===================== 1. 核心状态管理(仅总部财务) =====================
  538. const adminStore = useAdminStore();
  539. const { menuTree } = storeToRefs(adminStore);
  540. // 表格与分页数据
  541. const tableData = ref([]);
  542. const total = ref(0);
  543. const pageInfo = ref({ pageSize: 10, pageNum: 1 });
  544. const tableRef = ref(null)
  545. const scrollTableTop = () => {
  546. tableRef.value?.setScrollTop?.(0)
  547. }
  548. // 搜索条件
  549. const searchData = ref({});
  550. const defaultTime = [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)];
  551. const getTime = ref([]);
  552. // 标签页
  553. const activeTab = ref('pass');
  554. // 编辑弹窗状态
  555. const editFormisible = ref(false);
  556. const editFormData = ref({});
  557. const editFormRef = ref(null);
  558. const isEditGold = ref(false); // 金币产品标记
  559. const ifOnline = ref(false); // 在线支付标记
  560. // 辅助状态:图片上传、悬浮提示
  561. const uploadRef = ref(null);
  562. const showTooltip = ref(false);
  563. const tooltipContent = ref('');
  564. const tooltipLeft = ref(0);
  565. const tooltipTop = ref(0);
  566. // 退款确认弹窗
  567. const refundConfirmDialog = ref(false)
  568. const textContent = ref('')
  569. //退款弹窗
  570. const refundDialog = ref(false)
  571. const refundFormData = ref({})
  572. const openRefundDialog = () => {
  573. refundDialog.value = true
  574. closeConfirmRefund()
  575. }
  576. const closeRefundForm = () => {
  577. refundDialog.value = false
  578. refundFormData.value = {}
  579. }
  580. const isRefundGold = ref(false)
  581. const ifRefundGold = () => {
  582. if (refundFormData.value.goodsName === t('cash.coinRecharge')) {
  583. isRefundGold.value = true
  584. refundFormData.value.goodNum = 0
  585. } else {
  586. isRefundGold.value = false
  587. }
  588. }
  589. //导出相关
  590. const exportListVisible = ref(false)
  591. const EXPORT_LIST_POLL_INTERVAL = 3000
  592. let exportListPollingTimer = null
  593. const exportList = ref([])
  594. const exportListLoading = ref(false)
  595. const exportListRequesting = ref(false)
  596. const sortExportList = (list = []) => {
  597. return [...list].sort((a, b) => {
  598. return new Date(b.createTime).getTime() - new Date(a.createTime).getTime()
  599. })
  600. }
  601. const hasPendingExportTask = (list = []) => {
  602. const latestTask = sortExportList(list)[0]
  603. return latestTask ? latestTask.state === 0 || latestTask.state === 1 : false
  604. }
  605. const stopExportListPolling = () => {
  606. if (exportListPollingTimer) {
  607. clearInterval(exportListPollingTimer)
  608. exportListPollingTimer = null
  609. }
  610. }
  611. const startExportListPolling = () => {
  612. if (exportListPollingTimer) {
  613. return
  614. }
  615. exportListPollingTimer = setInterval(() => {
  616. if (!exportListVisible.value) {
  617. stopExportListPolling()
  618. return
  619. }
  620. getExportList({ showLoading: false, silentError: true })
  621. }, EXPORT_LIST_POLL_INTERVAL)
  622. }
  623. const exportExcel = async function () {
  624. let payCurrencySelect = '';
  625. let goodsName = '';
  626. // 处理时间范围
  627. if (getTime.value && getTime.value.length === 2) {
  628. searchData.value.startTime = moment(getTime.value[0]).format('YYYY-MM-DD HH:mm:ss');
  629. searchData.value.endTime = moment(getTime.value[1]).format('YYYY-MM-DD HH:mm:ss');
  630. } else {
  631. searchData.value.startTime = '';
  632. searchData.value.endTime = '';
  633. }
  634. // 处理产品名称(级联选择取最后一级)
  635. if (searchData.value.goodsName) {
  636. goodsName = searchData.value.goodsName[searchData.value.goodsName.length - 1];
  637. }
  638. // 处理付款币种(转ID)
  639. if (searchData.value.paymentCurrency) {
  640. payCurrencySelect = CurrencyForId(searchData.value.paymentCurrency);
  641. }
  642. // if (searchData.value.jwcode && !isNumber(searchData.value.jwcode)) {
  643. // ElMessage.error('精网号只能是数字')
  644. // return
  645. // }
  646. const cashRoleId = '2';
  647. const submitterMarket = normalizeSubmitterMarket(adminData.value.markets);
  648. searchData.value.status = 46;
  649. const params = {
  650. ...pageInfo.value,
  651. cashCollection: {
  652. ...searchData.value,
  653. submitterId: adminData.value.id,
  654. receivedMarket: MarketNameForId(submitterMarket),
  655. cashRoleId: cashRoleId,
  656. paymentCurrency: payCurrencySelect,
  657. submitterMarket: submitterMarket,
  658. goodsName: goodsName,
  659. market: MarketNameForId(searchData.value.market)
  660. }
  661. }
  662. const res = await API({ url: '/export/exportCash', data: params })
  663. if (res.code === 200) {
  664. ElMessage.success(t('elmessage.exportSuccess'))
  665. } else {
  666. ElMessage.error(res.msg || t('elmessage.exportFailed'))
  667. }
  668. }
  669. const openExportList = () => {
  670. exportListVisible.value = true
  671. }
  672. //获取导出列表
  673. const getExportList = async ({ showLoading = true, silentError = false } = {}) => {
  674. if (exportListRequesting.value) {
  675. return
  676. }
  677. if (showLoading) {
  678. exportListLoading.value = true
  679. }
  680. exportListRequesting.value = true
  681. try {
  682. const result = await API({ url: '/export/export' })
  683. if (result.code === 200) {
  684. const filteredData = result.data.filter(item => {
  685. return item.type === 13
  686. })
  687. exportList.value = sortExportList(filteredData)
  688. if (exportListVisible.value && hasPendingExportTask(exportList.value)) {
  689. startExportListPolling()
  690. } else {
  691. stopExportListPolling()
  692. }
  693. } else {
  694. stopExportListPolling()
  695. if (!silentError) {
  696. ElMessage.error(result.msg || t('elmessage.getExportListError'))
  697. }
  698. }
  699. } catch (error) {
  700. console.error('获取导出列表出错:', error)
  701. stopExportListPolling()
  702. if (!silentError) {
  703. ElMessage.error(t('elmessage.getExportListError'))
  704. }
  705. } finally {
  706. exportListRequesting.value = false
  707. if (showLoading) {
  708. exportListLoading.value = false
  709. }
  710. }
  711. }
  712. const downloadExportFile = (item) => {
  713. if (item.state === 2) {
  714. const link = document.createElement('a')
  715. link.href = item.url
  716. link.download = item.fileName
  717. link.click()
  718. } else {
  719. ElMessage.warning(t('elmessage.exportingInProgress'))
  720. }
  721. }
  722. //根据状态返回对应的标签类型
  723. const getTagType = (state) => {
  724. switch (state) {
  725. case 0:
  726. return 'info';
  727. case 1:
  728. return 'primary';
  729. case 2:
  730. return 'success';
  731. case 3:
  732. return 'danger';
  733. default:
  734. return 'info';
  735. }
  736. }
  737. //根据状态返回对应的标签文案
  738. const getTagText = (state) => {
  739. switch (state) {
  740. case 0:
  741. return t('elmessage.pendingExecution');
  742. case 1:
  743. return t('elmessage.executing');
  744. case 2:
  745. return t('elmessage.executed');
  746. case 3:
  747. return t('elmessage.errorExecution');
  748. default:
  749. return t('elmessage.unknownStatus');
  750. }
  751. }
  752. // 基础数据
  753. const adminData = ref({});
  754. const activityList = ref([]);
  755. // 付款币种和支付方式选项(客服专用)
  756. const customOptions = ref([
  757. t('cash.currency.usd'), // 美元(USD)
  758. t('cash.currency.hkd'), // 港币(HKD)
  759. t('cash.currency.sgd'), // 新币(SGD)
  760. t('cash.currency.myr'), // 马币(MYR)
  761. t('cash.currency.thb'), // 泰铢(THB)
  762. t('cash.currency.cad'), // 加币(CAD)
  763. t('cash.currency.vnd'), // 越南盾(VND)
  764. t('cash.currency.krw') // 韩元(KRW)
  765. ])
  766. // 支付类型选项 - 使用cash.payMethods下的键名
  767. const paytypeList = ref([
  768. t('cash.payMethods.stripe'), // Stripe
  769. t('cash.payMethods.paymentAsia'), // PaymentAsia
  770. t('cash.payMethods.ipay88'), // Ipay88
  771. t('cash.payMethods.grabpay'), // Grabpay
  772. t('cash.payMethods.nets'), // Nets
  773. t('cash.payMethods.transfer'), // E-Transfer
  774. t('cash.payMethods.iotPay'), // IOT Pay
  775. t('cash.payMethods.stripe3'), // Stripe3
  776. t('cash.payMethods.paypal'), // PayPal
  777. t('cash.payMethods.bankTransfer'),// 银行转账
  778. t('cash.payMethods.card'), // 刷卡
  779. t('cash.payMethods.cash'), // 现金
  780. t('cash.payMethods.check') // 支票
  781. ])
  782. const paytypeOptions = ref([...paytypeList.value]);
  783. // ===================== 2. 核心功能函数(仅总部财务) =====================
  784. //确认退款弹窗
  785. const openRefundConfirm = (row) => {
  786. textContent.value = t('common.willRefundOrder')
  787. refundConfirmDialog.value = true
  788. refundFormData.value = { ...row }
  789. ifRefundGold()
  790. console.log(row);
  791. }
  792. const closeConfirmRefund = () => {
  793. refundConfirmDialog.value = false
  794. textContent.value = ''
  795. }
  796. // 2.1 数据加载:获取总部财务订单列表
  797. const getlist = async () => {
  798. try {
  799. let payCurrencySelect = '';
  800. let goodsName = '';
  801. // 处理时间范围
  802. if (getTime.value && getTime.value.length === 2) {
  803. searchData.value.startTime = moment(getTime.value[0]).format('YYYY-MM-DD HH:mm:ss');
  804. searchData.value.endTime = moment(getTime.value[1]).format('YYYY-MM-DD HH:mm:ss');
  805. } else {
  806. searchData.value.startTime = '';
  807. searchData.value.endTime = '';
  808. }
  809. // 处理产品名称(级联选择取最后一级)
  810. if (searchData.value.goodsName) {
  811. goodsName = searchData.value.goodsName[searchData.value.goodsName.length - 1];
  812. }
  813. // 处理付款币种(转ID)
  814. if (searchData.value.paymentCurrency) {
  815. payCurrencySelect = CurrencyForId(searchData.value.paymentCurrency);
  816. }
  817. // 总部财务固定参数:角色ID=1,按标签页筛选状态
  818. const cashRoleId = '2';
  819. const receivedMarket = adminData.value.markets;
  820. if (activeTab.value === 'pass') searchData.value.status = 13;
  821. else if (activeTab.value === 'done') searchData.value.status = 46;
  822. // 地区处理
  823. const markets = ref(null)
  824. if (searchData.value.markets) {
  825. markets.value = searchData.value.markets[searchData.value.markets.length - 1]
  826. console.log('地区转换', markets.value)
  827. }
  828. if (searchData.value.jwcode) {
  829. const isPositiveInteger = /^[1-9]\d*$/.test(searchData.value.jwcode);
  830. if (!isPositiveInteger) {
  831. ElMessage.error(t('elmessage.checkJwcodeFormat'))
  832. return;
  833. }
  834. // 添加长度验证,超过8位提示错误
  835. if (searchData.value.jwcode.length > 8) {
  836. ElMessage.error(t('elmessage.limitJwcodeLength'))
  837. return;
  838. }
  839. }
  840. // if (searchData.value.jwcode && !isNumber(searchData.value.jwcode)) {
  841. // ElMessage.error('精网号只能是数字')
  842. // return
  843. // }
  844. const result = await request({
  845. url: '/cashCollection/selectCollection',
  846. data: {
  847. ...pageInfo.value,
  848. cashCollection: {
  849. ...searchData.value,
  850. submitterId: adminData.value.id,
  851. receivedMarket: MarketNameForId(receivedMarket),
  852. cashRoleId: cashRoleId,
  853. paymentCurrency: payCurrencySelect,
  854. submitterMarket: normalizeSubmitterMarket(receivedMarket),
  855. goodsName: goodsName,
  856. //market: MarketNameForId(searchData.value.market)
  857. market: markets.value
  858. }
  859. }
  860. });
  861. if (result.code === 200) {
  862. tableData.value = result.data.list;
  863. await nextTick()
  864. scrollTableTop()
  865. total.value = result.data.total;
  866. } else {
  867. ElMessage.error(t('elmessage.orderDataLoadFailed'));
  868. }
  869. } catch (error) {
  870. console.error('总部财务订单列表请求异常:', error);
  871. ElMessage.error(t('elmessage.inNetworkError'));
  872. }
  873. };
  874. //重置退款
  875. const resetRefund = () => {
  876. refundFormData.value.refundModel = ''
  877. refundFormData.value.refundReason = ''
  878. }
  879. //提交退款
  880. const submitRefund = async () => {
  881. try {
  882. if (refundFormData.value.goodsName != t('cash.coinRecharge')) {
  883. return ElMessage.error(t('elmessage.onlineDataSupport'));
  884. }
  885. if (!refundFormData.value.refundModel) {
  886. return ElMessage.error(t('elmessage.selectRefundModel'));
  887. }
  888. if (!refundFormData.value.refundReason) {
  889. return ElMessage.error(t('elmessage.refundReasonPlaceholder'));
  890. }
  891. // 添加精网号验证
  892. if (refundFormData.value.jwcode) {
  893. const isPositiveInteger = /^[1-9]\d*$/.test(refundFormData.value.jwcode);
  894. if (!isPositiveInteger) {
  895. return ElMessage.error(t('elmessage.jwcodePositiveError'));
  896. }
  897. if (refundFormData.value.jwcode.length > 8) {
  898. return ElMessage.error(t('elmessage.limitJwcodeLength'));
  899. }
  900. }
  901. if (refundFormData.value.refundModel == 0) {
  902. refundFormData.value.partRefundGold = refundFormData.value.permanentGold,
  903. refundFormData.value.partRefundFree = refundFormData.value.freeGold
  904. }
  905. const result = await request({
  906. url: '/Money/addOnline',
  907. data: {
  908. jwcode: refundFormData.value.jwcode,
  909. name: refundFormData.value.name,
  910. market: refundFormData.value.marketName,
  911. submitterId: adminData.value.id,
  912. submitterMarket: adminData.value.markets,
  913. remark: refundFormData.value.remark,
  914. refundReason: refundFormData.value.refundReason,
  915. refundModel: refundFormData.value.refundModel,
  916. id: refundFormData.value.id,
  917. orderCode: refundFormData.value.orderCode,
  918. permanentGold: (refundFormData.value.permanentGold) * 100 || 0,
  919. freeGold: (refundFormData.value.freeGold) * 100 || 0,
  920. partRefundGold: (refundFormData.value.partRefundGold) * 100 || 0,
  921. partRefundFree: (refundFormData.value.partRefundFree) * 100 || 0,
  922. }
  923. })
  924. if (result.code == 200) {
  925. ElMessage.success(t('elmessage.addRefundSuccess'))
  926. getlist()
  927. closeRefundForm()
  928. } else {
  929. ElMessage.error(result.msg)
  930. getlist()
  931. }
  932. console.log('返回参数:', result);
  933. } catch (error) {
  934. console.log(error);
  935. }
  936. }
  937. // 2.2 搜索与重置
  938. const search = () => {
  939. getlist();
  940. };
  941. const reset = () => {
  942. searchData.value = {};
  943. // 重置页码
  944. pageInfo.value.pageNum = 1;
  945. getlist();
  946. };
  947. // 2.3 标签页切换
  948. const navigateTo = async (tab) => {
  949. activeTab.value = tab;
  950. await getlist();
  951. };
  952. // 2.4 审核功能
  953. // 打开审核弹窗
  954. const openAuditForm = (row) => {
  955. auditFormData.value = { ...row, market: row.marketName };
  956. ifGold(auditFormData.value)
  957. console.log('isGold', isGold.value);
  958. auditFormisible.value = true;
  959. };
  960. // 关闭审核弹窗
  961. const closeAuditForm = () => {
  962. ifReject.value = false;
  963. auditFormisible.value = false;
  964. auditFormData.value = {};
  965. };
  966. // 审核通过
  967. const handelAudit = async () => {
  968. try {
  969. const result = await request({
  970. url: '/cashAudit/collectionAudit',
  971. data: {
  972. orderCode: auditFormData.value.orderCode,
  973. action: 1,
  974. auditId: adminData.value.id
  975. }
  976. });
  977. if (result.code === 200) {
  978. ElMessage.success(t('elmessage.approveSuccess'));
  979. getlist();
  980. closeAuditForm();
  981. } else {
  982. ElMessage.error(result.msg || t('elmessage.approveFailed'));
  983. }
  984. } catch (error) {
  985. console.error('审核通过请求异常:', error);
  986. ElMessage.error(t('elmessage.inNetworkError'));
  987. }
  988. };
  989. //判断产品类型
  990. const isGold = ref(false)
  991. const ifGold = (data) => {
  992. console.log('data', data);
  993. if (data.goodsName === t('cash.coinRecharge')) {
  994. isGold.value = true
  995. } else {
  996. isGold.value = false
  997. }
  998. }
  999. // 2.5 编辑手续费功能
  1000. // 打开编辑弹窗
  1001. const openEditForm = (row) => {
  1002. editFormData.value = { ...row };
  1003. // 区分金币产品与普通产品
  1004. if (row.goodsName === t('cash.coinRecharge')) isEditGold.value = true;
  1005. else isEditGold.value = false;
  1006. // 在线支付订单允许修改付款信息
  1007. if (row.status === 3) ifOnline.value = true;
  1008. else ifOnline.value = false;
  1009. editFormisible.value = true;
  1010. };
  1011. // 关闭编辑弹窗
  1012. const closeEditForm = () => {
  1013. editFormisible.value = false;
  1014. editFormData.value = {};
  1015. editFormRef.value.resetFields();
  1016. };
  1017. // 提交编辑(补充手续费)
  1018. const submitEditForm = async () => {
  1019. try {
  1020. await editFormRef.value.validate();
  1021. if (editFormData.value.paymentCurrency != editFormData.value.receivedCurrency) {
  1022. ElMessage.error(t('elmessage.currencyMismatch'))
  1023. return
  1024. }
  1025. // 处理时间格式
  1026. if (editFormData.value.receivedTime) {
  1027. editFormData.value.receivedTime = moment(editFormData.value.receivedTime).format('YYYY-MM-DD HH:mm:ss');
  1028. }
  1029. const result = await request({
  1030. url: '/cashCollection/complete',
  1031. data: {
  1032. orderCode: editFormData.value.orderCode,
  1033. handlingCharge: editFormData.value.handlingCharge * 100 || null, // 转为分单位
  1034. paymentCurrency: CurrencyForId(editFormData.value.paymentCurrency),
  1035. paymentAmount: editFormData.value.paymentAmount * 100, // 转为分单位
  1036. receivedCurrency: CurrencyForId(editFormData.value.receivedCurrency),
  1037. receivedAmount: editFormData.value.receivedAmount * 100 || null, // 转为分单位
  1038. receivedTime: editFormData.value.receivedTime,
  1039. receivedRemark: editFormData.value.receivedRemark || ''
  1040. }
  1041. });
  1042. if (result.code === 200) {
  1043. ElMessage.success(t('elmessage.editSuccess'));
  1044. getlist();
  1045. closeEditForm();
  1046. } else {
  1047. ElMessage.error(result.msg || t('elmessage.editFailed'));
  1048. }
  1049. } catch (error) {
  1050. console.error('编辑提交请求异常:', error);
  1051. ElMessage.error(t('elmessage.formValidationFailed'));
  1052. }
  1053. };
  1054. // 2.6 辅助功能
  1055. // 图片预览
  1056. const previewImage = (imageUrl) => {
  1057. if (!imageUrl) return;
  1058. const imageElement = document.createElement('img');
  1059. imageElement.src = imageUrl;
  1060. imageElement.style.maxWidth = '80vw';
  1061. imageElement.style.maxHeight = '80vh';
  1062. imageElement.style.objectFit = 'contain';
  1063. const viewer = document.createElement('div');
  1064. viewer.style.position = 'fixed';
  1065. viewer.style.top = '0';
  1066. viewer.style.left = '0';
  1067. viewer.style.width = '100vw';
  1068. viewer.style.height = '100vh';
  1069. viewer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  1070. viewer.style.display = 'flex';
  1071. viewer.style.justifyContent = 'center';
  1072. viewer.style.alignItems = 'center';
  1073. viewer.style.zIndex = '9999';
  1074. viewer.appendChild(imageElement);
  1075. // 点击关闭预览
  1076. viewer.addEventListener('click', () => {
  1077. document.body.removeChild(viewer);
  1078. });
  1079. document.body.appendChild(viewer);
  1080. };
  1081. // 2.7 基础数据加载(初始化)
  1082. // 获取管理员信息
  1083. const getAdminData = async () => {
  1084. try {
  1085. const result = await API({ url: '/admin/userinfo', data: {} });
  1086. adminData.value = result;
  1087. // 超级管理员判断(仅用于角色切换权限)
  1088. // if (adminData.value.roleId === 2) {
  1089. // // 管理员所属地区提示
  1090. // if (adminData.value.markets === '总部' || adminData.value.markets === '研发部') {
  1091. // ElMessageBox.alert(
  1092. // '管理员账号仅显示所属地区的财务数据,请确认地区设置',
  1093. // '温馨提示',
  1094. // { type: 'warning' }
  1095. // );
  1096. // }
  1097. // }
  1098. } catch (error) {
  1099. console.error('管理员信息获取失败:', error);
  1100. ElMessage.error(t('elmessage.adminInfoLoadFailed'));
  1101. }
  1102. };
  1103. // 获取活动列表
  1104. const getActivitys = async () => {
  1105. try {
  1106. const result = await API({ url: '/cashCollection/getActivityList', data: {} });
  1107. if (result.code === 200) {
  1108. activityList.value = result.data;
  1109. } else {
  1110. ElMessage.error(t('elmessage.activityLoadFailed'));
  1111. }
  1112. } catch (error) {
  1113. console.error('活动列表获取失败:', error);
  1114. ElMessage.error(t('elmessage.activityDataLoadFailed'));
  1115. }
  1116. };
  1117. // 2.8 节流函数(防止重复提交)
  1118. const throttledsubmitEditForm = _.throttle(submitEditForm, 3000, { trailing: false });
  1119. const throttledsubmitRefund = _.throttle(submitRefund, 5000, {
  1120. trailing: false
  1121. })
  1122. // 2.9 页面初始化
  1123. onMounted(async () => {
  1124. await getAdminData();
  1125. await getActivitys();
  1126. // 初始化加载总部财务订单数据
  1127. await getlist();
  1128. // 菜单权限校验(确保总部财务权限)
  1129. if (!hasMenuPermission(menuTree.value, 91)) {
  1130. ElMessageBox.alert(
  1131. t('elmessage.noPermissionText'),
  1132. t('elmessage.permissionPrompt'),
  1133. { type: 'error' }
  1134. ).then(() => {
  1135. window.history.back();
  1136. });
  1137. }
  1138. //背景预加载
  1139. const bgImg = new Image();
  1140. bgImg.src = '/src/assets/receive-recall.png';
  1141. getMarket()
  1142. });
  1143. // 2.10 未使用函数占位(避免报错)
  1144. const beforeUpload = () => true;
  1145. const handelImgErr = () => { };
  1146. const handleImageChange = () => { };
  1147. const customUpload = () => { };
  1148. const handleDatePickerChange = () => { };
  1149. const disabledDate = () => false;
  1150. const handleSortChange = () => { };
  1151. const getMarket = async function () {
  1152. try {
  1153. const result = await API({
  1154. url: '/market/selectMarket',
  1155. })
  1156. console.log('看看地区树', result)
  1157. const transformTree = (nodes) => {
  1158. const allChildren = nodes.flatMap(node => node.children || []);
  1159. return allChildren.map(child => {
  1160. const grandchildren = child.children && child.children.length
  1161. ? transformTree([child])
  1162. : null;
  1163. return {
  1164. value: child.id,
  1165. label: child.name,
  1166. children: grandchildren
  1167. };
  1168. });
  1169. };
  1170. market.value = transformTree(result.data)
  1171. console.log('转换后的地区树==============', market.value)
  1172. } catch (error) {
  1173. console.log('请求失败', error)
  1174. }
  1175. }
  1176. const handlePagination = (type, val) => {
  1177. if (type === 'size') pageInfo.value.pageSize = val;
  1178. else pageInfo.value.pageNum = val;
  1179. getlist();
  1180. };
  1181. watch(exportListVisible, (visible) => {
  1182. if (visible) {
  1183. getExportList()
  1184. } else {
  1185. stopExportListPolling()
  1186. }
  1187. })
  1188. onBeforeUnmount(() => {
  1189. stopExportListPolling()
  1190. })
  1191. </script>
  1192. <style scoped lang="scss">
  1193. .content {
  1194. height: 100%;
  1195. width: 80vw;
  1196. .card1 {
  1197. width: 100%;
  1198. background: #F3FAFE;
  1199. .rowItem {
  1200. display: flex;
  1201. width: 15vw;
  1202. align-items: center;
  1203. justify-content: center;
  1204. margin-right: 0.5vw;
  1205. }
  1206. el-button {
  1207. margin-left: 10px;
  1208. }
  1209. .row1 {
  1210. height: 4vh;
  1211. width: 80vw;
  1212. display: flex;
  1213. min-height: 40px;
  1214. }
  1215. .row2 {
  1216. height: 4vh;
  1217. width: 80vw;
  1218. display: flex;
  1219. align-items: center;
  1220. min-height: 40px;
  1221. .left {
  1222. height: 4vh;
  1223. width: 58vw;
  1224. display: flex;
  1225. min-height: 40px;
  1226. }
  1227. }
  1228. }
  1229. .div-card2 {
  1230. width: 100%;
  1231. margin-top: 2vh;
  1232. .card2 {
  1233. background: #E7F4FD;
  1234. }
  1235. .btns {
  1236. display: flex;
  1237. align-items: center;
  1238. justify-content: space-between;
  1239. padding-bottom: 10px;
  1240. .tabs {
  1241. min-width: 300px;
  1242. .btnItem {
  1243. margin-left: 10px;
  1244. border-radius: 5px;
  1245. }
  1246. }
  1247. .info-tooltip {
  1248. display: flex;
  1249. align-items: center;
  1250. }
  1251. .service-icon {
  1252. color: #666;
  1253. cursor: pointer;
  1254. font-size: 18px;
  1255. transition: all 0.3s ease;
  1256. padding: 4px;
  1257. border-radius: 50%;
  1258. }
  1259. }
  1260. .table {
  1261. margin: 10px;
  1262. border-radius: 20px;
  1263. .ellipsis-container {
  1264. position: relative;
  1265. width: 100%;
  1266. .ellipsis-text {
  1267. display: inline-block;
  1268. width: 100%;
  1269. white-space: nowrap;
  1270. overflow: hidden;
  1271. text-overflow: ellipsis;
  1272. cursor: pointer;
  1273. }
  1274. .custom-tooltip {
  1275. position: fixed;
  1276. z-index: 9999;
  1277. padding: 8px 12px;
  1278. width: 200px;
  1279. background-color: #E4F0FC;
  1280. color: #333333;
  1281. border: 1px solid #e5e7eb;
  1282. border-radius: 4px;
  1283. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  1284. min-height: 30px;
  1285. max-height: 300px;
  1286. overflow-y: auto;
  1287. font-size: 14px;
  1288. line-height: 1.5;
  1289. }
  1290. }
  1291. }
  1292. .pagination {
  1293. margin-top: 10px;
  1294. padding: 10px 10px;
  1295. }
  1296. }
  1297. :deep(.adddialog) {
  1298. min-width: 500px;
  1299. background-color: #F3FAFE !important;
  1300. margin-top: 8vh;
  1301. border-radius: 8px;
  1302. .addForm {
  1303. padding: 0 60px 1vh 60px;
  1304. .el-date-editor {
  1305. display: flex;
  1306. flex: 1;
  1307. }
  1308. .pic {
  1309. display: flex;
  1310. align-items: center;
  1311. .uploader {
  1312. height: 80px;
  1313. width: 80px;
  1314. .el-upload {
  1315. height: 100%;
  1316. width: 100%;
  1317. }
  1318. }
  1319. .picText {
  1320. color: #999999;
  1321. font-family: "PingFang SC";
  1322. font-size: 10px;
  1323. font-style: normal;
  1324. font-weight: 400;
  1325. line-height: 20px;
  1326. margin-left: 10px;
  1327. }
  1328. }
  1329. }
  1330. .dialog-footer {
  1331. display: flex;
  1332. justify-content: center;
  1333. padding-bottom: 1.5vh;
  1334. }
  1335. }
  1336. :deep(.editdialog) {
  1337. min-width: 990px;
  1338. background-color: #F3FAFE !important;
  1339. margin-top: 8vh;
  1340. border-radius: 8px;
  1341. .editForm {
  1342. padding: 0 60px 1vh 60px;
  1343. .el-date-editor {
  1344. display: flex;
  1345. flex: 1;
  1346. }
  1347. .pic {
  1348. display: flex;
  1349. align-items: center;
  1350. .uploader {
  1351. height: 80px;
  1352. width: 80px;
  1353. .el-upload {
  1354. height: 100%;
  1355. width: 100%;
  1356. }
  1357. }
  1358. .picText {
  1359. color: #999999;
  1360. font-family: "PingFang SC";
  1361. font-size: 10px;
  1362. font-style: normal;
  1363. font-weight: 400;
  1364. line-height: 20px;
  1365. margin-left: 10px;
  1366. }
  1367. }
  1368. }
  1369. .content {
  1370. display: flex;
  1371. height: 100%;
  1372. width: 100%;
  1373. .left {
  1374. min-width: 500px;
  1375. }
  1376. .right {
  1377. flex: 1;
  1378. .editFormRighrt {
  1379. padding: 0 60px 0 40px;
  1380. .el-date-editor {
  1381. display: flex;
  1382. flex: 1;
  1383. }
  1384. }
  1385. .editBtns {
  1386. display: flex;
  1387. justify-content: center;
  1388. margin-top: 60px;
  1389. .txt {
  1390. color: #f3fafe;
  1391. text-align: center;
  1392. font-family: "PingFang SC";
  1393. font-size: 14px;
  1394. font-style: normal;
  1395. font-weight: 700;
  1396. line-height: 22px;
  1397. }
  1398. .editBtn1 {
  1399. min-width: 80px;
  1400. padding: 5px 12px;
  1401. justify-content: center;
  1402. align-items: center;
  1403. gap: 4px;
  1404. border-radius: 4px;
  1405. background: #7E91FF;
  1406. border: none;
  1407. box-shadow: 0 0 4px 0 #00000040;
  1408. }
  1409. .editBtn2 {
  1410. display: flex;
  1411. width: 80px;
  1412. min-width: 80px;
  1413. padding: 5px 12px;
  1414. justify-content: center;
  1415. align-items: center;
  1416. gap: 4px;
  1417. border-radius: 4px;
  1418. background: #2741DE;
  1419. border: none;
  1420. box-shadow: 0 0 4px 0 #00000040;
  1421. margin-left: 60px;
  1422. }
  1423. }
  1424. }
  1425. }
  1426. }
  1427. :deep(.adddialog .el-form-item__label) {
  1428. min-width: 100px;
  1429. width: auto;
  1430. font-weight: 800;
  1431. padding-bottom: 15px;
  1432. }
  1433. .refundDialog {
  1434. .left {
  1435. width: 50%;
  1436. height: 70vh;
  1437. min-height: 700px;
  1438. padding: 0 2vw;
  1439. .add-item {
  1440. display: flex;
  1441. align-items: center;
  1442. margin-bottom: 1vh;
  1443. }
  1444. .image {
  1445. width: 4vw !important;
  1446. height: 4vw !important;
  1447. }
  1448. }
  1449. .right {
  1450. width: 50%;
  1451. height: 50vh;
  1452. .add-item {
  1453. display: flex;
  1454. align-items: center;
  1455. margin-bottom: 1vh;
  1456. }
  1457. }
  1458. }
  1459. }
  1460. // 表格样式统一
  1461. :deep(.el-table__header-wrapper),
  1462. :deep(.el-table__body-wrapper),
  1463. :deep(.el-table__cell),
  1464. :deep(.el-table__body td) {
  1465. background-color: #F3FAFE !important;
  1466. }
  1467. :deep(.el-table__header th) {
  1468. background-color: #F3FAFE !important;
  1469. }
  1470. :deep(.el-table__row:hover > .el-table__cell) {
  1471. background-color: #E5EBFE !important;
  1472. }
  1473. // 驳回理由弹窗样式
  1474. .reject-reason-box {
  1475. --el-message-box-height: 500px;
  1476. }
  1477. .reject-reason-box .el-message-box__content {
  1478. max-height: 350px;
  1479. overflow-y: auto;
  1480. white-space: pre-wrap;
  1481. }
  1482. .recallDialog {
  1483. //撤回弹窗提示
  1484. height: 392px;
  1485. width: 700px;
  1486. background-image: url('/src/assets/receive-recall.png');
  1487. position: fixed; // 固定定位,相对于浏览器窗口
  1488. top: 50%; // 距离顶部50%
  1489. left: 50%; // 距离左侧50%
  1490. transform: translate(-50%, -50%); // 向左、向上平移自身宽高的50%,实现居中
  1491. z-index: 1000; // 确保在其他元素上层显示
  1492. .close {
  1493. position: absolute;
  1494. left: 625px;
  1495. top: 20px;
  1496. height: 38px;
  1497. width: 38px;
  1498. opacity: 0;
  1499. .Btn {
  1500. height: 100%;
  1501. width: 100%;
  1502. border-radius: 10px;
  1503. }
  1504. }
  1505. .text {
  1506. position: absolute;
  1507. left: 185px;
  1508. top: 190px;
  1509. height: 67px;
  1510. width: 500px;
  1511. .txt {
  1512. height: 100%;
  1513. width: 100%;
  1514. color: #001a42;
  1515. font-family: "PingFang SC";
  1516. font-size: 48px;
  1517. font-style: normal;
  1518. font-weight: 900;
  1519. line-height: normal;
  1520. }
  1521. }
  1522. .cancle {
  1523. position: absolute;
  1524. left: 185px;
  1525. top: 304px;
  1526. height: 55px;
  1527. width: 150px;
  1528. opacity: 0;
  1529. .Btn {
  1530. height: 100%;
  1531. width: 100%;
  1532. border-radius: 20px;
  1533. }
  1534. }
  1535. .confirm {
  1536. position: absolute;
  1537. left: 375px;
  1538. top: 304px;
  1539. height: 55px;
  1540. width: 150px;
  1541. opacity: 0;
  1542. .Btn {
  1543. height: 100%;
  1544. width: 100%;
  1545. border-radius: 20px;
  1546. }
  1547. }
  1548. }
  1549. </style>