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.

1770 lines
70 KiB

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