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.

1488 lines
54 KiB

3 months ago
3 months ago
3 months ago
6 days ago
6 days ago
3 months ago
3 months ago
3 months ago
6 days ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
1 month ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
5 days ago
5 days ago
6 days ago
5 days ago
6 days ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
6 days ago
3 months ago
5 days ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
  1. <script setup>
  2. import { ref, reactive, onMounted, nextTick, toRaw } from 'vue'
  3. import { useRoute } from 'vue-router'
  4. import { ElMessage, ElMessageBox } from 'element-plus'
  5. import request from '@/util/http.js'
  6. import dayjs from 'dayjs'
  7. import { useI18n } from 'vue-i18n'
  8. import { Moneyfunds, refundOnline, exportFunds } from '@/api/cash/financialAccount.js'
  9. import { useAdminStore } from '@/store/index.js'
  10. import { storeToRefs } from 'pinia'
  11. import _ from 'lodash';
  12. import { normalizePayType,MarketNameForId, CurrencyForId, } from '@/views/moneyManage/receiveDetail/utils/staticData.js'
  13. import { isTemplate } from 'element-plus/es/utils/index.mjs'
  14. import { Row } from 'vxe-pc-ui'
  15. import CashManagement from '@/components/workspace/CashManagement.vue'
  16. import { permissionMapping, hasMenuPermission } from "@/utils/menuTreePermission.js"
  17. const adminStore = useAdminStore()
  18. const { adminData, menuTree,flag } = storeToRefs(adminStore)
  19. const { t } = useI18n()
  20. const route = useRoute()
  21. const paytypeList = [
  22. t('cash.payMethods.stripe'), // Stripe
  23. t('cash.payMethods.paymentAsia'), // PaymentAsia
  24. t('cash.payMethods.stripe2'), // Stripe2
  25. t('cash.payMethods.ipay88'), // Ipay88
  26. t('cash.payMethods.grabpay'), // Grabpay
  27. t('cash.payMethods.nets'), // Nets
  28. t('cash.payMethods.transfer'), // E-Transfer
  29. t('cash.payMethods.iotPay'), // IOT Pay
  30. t('cash.payMethods.stripe3'), // Stripe3
  31. t('cash.payMethods.paypal'), // PayPal
  32. t('cash.payMethods.paysolution'), // Paysolution
  33. t('cash.payMethods.bankTransfer'),// 银行转账
  34. t('cash.payMethods.card'), // 刷卡
  35. t('cash.payMethods.cash'), // 现金
  36. t('cash.payMethods.check'), // 支票
  37. ]
  38. // 新增流水权限
  39. const hasNewTransactionFlow = ref(false)
  40. // 初始化权限状态
  41. const initPermissions = async () => {
  42. if (!menuTree.value || !menuTree.value.length) return;
  43. // 新增流水权限
  44. hasNewTransactionFlow.value = hasMenuPermission(menuTree.value, permissionMapping.new_transaction_flow);
  45. };
  46. const payPlatformOptions = ref([...paytypeList])
  47. const marketFilter = (value) => {
  48. const map = {
  49. 4: t('cash.markets.Singapore'),
  50. 5: t('cash.markets.Malaysia'),
  51. 13:t('cash.markets.HongKong'),
  52. 24016: t('cash.markets.Canada'),
  53. 24018:t('cash.markets.Thailand'),
  54. 24022:t('cash.markets.VietnamHCM'),
  55. 24033:t('cash.markets.Beijing'),
  56. };
  57. return map[value] || '-';
  58. };
  59. // 地区树
  60. const marketOptions = ref([])
  61. // 查询参数
  62. const queryParams = reactive({
  63. jwcode: '',
  64. markets: [], // 下拉多选
  65. performanceMarkets:[],
  66. timeRange: [], // [startTime, endTime]
  67. payType: '',
  68. orderCode: '',
  69. pageNum: 1,
  70. pageSize: 20,
  71. platformSelection: []
  72. })
  73. const total = ref(0)
  74. const tableData = ref([])
  75. const tableRef = ref(null)
  76. const scrollTableTop = () => {
  77. tableRef.value?.setScrollTop?.(0)
  78. }
  79. const loading = ref(false)
  80. // 转换树形结构(参考 coinConsumeDetail.vue)
  81. const transformTree = (nodes) => {
  82. const allChildren = nodes.flatMap(node => node.children || []);
  83. return allChildren.map(child => {
  84. const grandchildren = child.children && child.children.length
  85. ? transformTree([child])
  86. : null;
  87. return {
  88. value: child.id,
  89. label: child.name,
  90. children: grandchildren
  91. };
  92. });
  93. };
  94. // 获取地区数据
  95. const getMarket = async () => {
  96. try {
  97. const result = await request({ url: '/market/selectMarket' });
  98. if (result && result.data) {
  99. marketOptions.value = transformTree(result.data)
  100. }
  101. } catch (error) {
  102. console.error('获取地区失败', error)
  103. }
  104. }
  105. const formatStatuses = (statuses) => {
  106. // 情况1:非数组/空值 → 返回空数组
  107. if (!Array.isArray(statuses)) {
  108. return [];
  109. }
  110. // 情况2:数组中包含 null 或 undefined → 返回空数组
  111. if (statuses.some(item => item === null || item === undefined)) {
  112. return [];
  113. }
  114. // 情况3:正常数组 → 返回原数组
  115. return statuses;
  116. };
  117. const performanceMarket = ref([
  118. t('cash.markets.Malaysia'), // 马来西亚
  119. t('cash.markets.HongKong'), // 香港
  120. t('cash.markets.Singapore'), // 新加坡
  121. t('cash.markets.Thailand'), // 泰国
  122. t('cash.markets.VietnamHCM'), // 越南HCM
  123. t('cash.markets.Canada'), // 加拿大
  124. t('cash.markets.Beijing') // 北京
  125. ])
  126. const getPayPlatformOptions = async () => {
  127. try {
  128. const res = await request({
  129. url: '/market/getAreaPayTypeTree',
  130. method: 'POST'
  131. })
  132. if (res.code === 200) {
  133. payPlatformOptionsList.value = res.data
  134. console.log('支付平台数据加载成功:', payPlatformOptionsList.value)
  135. } else {
  136. ElMessage.error(res.msg || t('elmessage.getDataFailed'))
  137. }
  138. } catch (error) {
  139. console.error('请求支付平台数据出错:', error)
  140. ElMessage.error(t('elmessage.networkError'))
  141. }
  142. }
  143. // 查询列表
  144. const fetchData = async () => {
  145. loading.value = true
  146. try {
  147. //转换areaPayTypeList参数结构
  148. const areaPayTypeList = [];
  149. const options = toRaw(payPlatformOptionsList.value);
  150. const selectedPaths = queryParams.platformSelection;
  151. if (selectedPaths.length > 0 && options.length > 0) {
  152. selectedPaths.forEach(path => {
  153. const areaId = path[0];
  154. const payMethodId = path[path.length - 1];
  155. if (path.length === 1) {
  156. const countryNode = options.find(c => c.id === areaId);
  157. if (countryNode && countryNode.children) {
  158. countryNode.children.forEach(child => {
  159. areaPayTypeList.push({
  160. areaId: areaId,
  161. payType: child.name
  162. });
  163. });
  164. }
  165. }
  166. else {
  167. const countryNode = options.find(c => c.id === areaId);
  168. if (countryNode && countryNode.children) {
  169. const methodNode = countryNode.children.find(m => m.id === payMethodId);
  170. if (methodNode) {
  171. areaPayTypeList.push({
  172. areaId: areaId,
  173. payType: methodNode.name
  174. });
  175. }
  176. }
  177. }
  178. });
  179. }
  180. //转换performanceMarket参数结构
  181. let performanceMarkets = [];
  182. const selectedNames = queryParams.performanceMarket;
  183. if (Array.isArray(selectedNames) && selectedNames.length > 0) {
  184. performanceMarkets = selectedNames
  185. .map(name => {
  186. if (!name) return null;
  187. const id = MarketNameForId(name);
  188. return id;
  189. })
  190. .filter(id => id !== null && id !== 'null'); // 过滤无效值
  191. }
  192. const params = {
  193. pageNum: queryParams.pageNum,
  194. pageSize: queryParams.pageSize,
  195. fundsDTO: {
  196. jwcode: queryParams.jwcode,
  197. localMarket: queryParams.markets,
  198. performanceMarkets:performanceMarkets,
  199. areaPayTypeList: areaPayTypeList,
  200. startTime: queryParams.timeRange?.[0] ? dayjs(queryParams.timeRange[0]).format('YYYY-MM-DD HH:mm:ss') : '',
  201. endTime: queryParams.timeRange?.[1] ? dayjs(queryParams.timeRange[1]).format('YYYY-MM-DD HH:mm:ss') : '',
  202. payType: normalizePayType(queryParams.payType || ''),
  203. orderCode: queryParams.orderCode,
  204. markets: [],
  205. }
  206. }
  207. console.log('查询参数:', params)
  208. const res = await Moneyfunds(params)
  209. if (res.code == 200) {
  210. tableData.value = res.data.list || []
  211. await nextTick()
  212. scrollTableTop()
  213. total.value = res.data.total || 0
  214. loading.value = false
  215. } else {
  216. ElMessage.error(res.msg || t('elmessage.getDataFailed'))
  217. loading.value = false
  218. }
  219. } catch (error) {
  220. console.error(error)
  221. loading.value = false
  222. ElMessage.error(t('elmessage.getDataFailed'))
  223. }
  224. }
  225. const handleSearch = () => {
  226. queryParams.pageNum = 1
  227. fetchData()
  228. }
  229. const handleReset = () => {
  230. queryParams.jwcode = ''
  231. queryParams.performanceMarket = ''
  232. queryParams.timeRange = null
  233. queryParams.payType = ''
  234. queryParams.orderCode = ''
  235. queryParams.platformSelection=[]
  236. handleSearch()
  237. }
  238. const handlePageSizeChange = (val) => {
  239. queryParams.pageSize = val
  240. fetchData()
  241. }
  242. const handleCurrentChange = (val) => {
  243. queryParams.pageNum = val
  244. fetchData()
  245. }
  246. // 退款操作
  247. const openRefundConfirm = () => {
  248. showDetail.value=false
  249. textContent.value = t('common.willRefundOrder') + '?'
  250. refundConfirmDialog.value = true
  251. refundFormData.value = {
  252. ...formDataRow.value,
  253. oldpermanentGold: formDataRow.value.permanentGold || formDataRow.value.gold || 0,//退款永久金币
  254. oldfreeGold: formDataRow.value.freeGold || formDataRow.value.free || 0,//退款免费金币
  255. permanentGold: null,
  256. freeGold: null,
  257. }
  258. }
  259. const openRefundDialog = () => {
  260. refundDialog.value = true
  261. closeConfirmRefund()
  262. }
  263. const closeConfirmRefund = () => {
  264. refundConfirmDialog.value = false
  265. textContent.value = ''
  266. }
  267. const refundConfirmDialog = ref(false)
  268. const textContent = ref('')
  269. const refundDialog = ref(false)
  270. const refundFormData = ref({})
  271. const resetRefund = () => {
  272. refundFormData.value.refundModel = ''
  273. refundFormData.value.refundReason = ''
  274. refundFormData.value.permanentGold = null
  275. refundFormData.value.freeGold = null
  276. }
  277. const handleRefund = async () => {
  278. try {
  279. if(refundFormData.value.status == 6){
  280. ElMessage.error(t('elmessage.refundagain'))
  281. return
  282. }
  283. if (refundFormData.value.refundModel == 1) {
  284. if (Number(refundFormData.value.permanentGold || 0) > Number(refundFormData.value.oldpermanentGold || 0)) {
  285. ElMessage.error(t('elmessage.limitRefundGoldNotExceedOriginal'))
  286. return
  287. }
  288. if (Number(refundFormData.value.freeGold || 0) > Number(refundFormData.value.oldfreeGold || 0)) {
  289. ElMessage.error(t('elmessage.limitRefundGoldNotExceedOriginal'))
  290. return
  291. }
  292. }
  293. if (refundFormData.value.refundModel == 0) {
  294. refundFormData.value.permanentGold = refundFormData.value.oldpermanentGold
  295. refundFormData.value.freeGold = refundFormData.value.oldfreeGold
  296. }
  297. let params = {
  298. jwcode: refundFormData.value.jwcode,
  299. name: refundFormData.value.name,
  300. market: refundFormData.value.marketName,
  301. submitterMarket: adminData.value.markets,
  302. remark: refundFormData.value.remark,
  303. originalOrderId: refundFormData.value.id,
  304. refundReason: refundFormData.value.refundReason,
  305. refundModel: refundFormData.value.refundModel,
  306. orderCode: refundFormData.value.orderCode,
  307. submitterId: adminData.value.id,
  308. permanentGold: (refundFormData.value.permanentGold) * 100 || 0,
  309. handlingCharge: refundFormData.value.handlingCharge == null ? null : refundFormData.value.handlingCharge * 100,
  310. freeGold: (refundFormData.value.freeGold) * 100 || 0,
  311. partRefundGold: (refundFormData.value.permanentGold * 100 || 0),
  312. partRefundFree: (refundFormData.value.freeGold * 100 || 0),
  313. payType: refundFormData.value.payType,
  314. }
  315. console.log('这是退款参数:', params);
  316. const res = await refundOnline(params)
  317. if (res.code == 200) {
  318. ElMessage.success(t('elmessage.submitSuccess'))
  319. refundDialog.value = false
  320. fetchData()
  321. } else {
  322. ElMessage.error(res.msg || t('refund.refundFailed'))
  323. }
  324. } catch (error) {
  325. console.error(error)
  326. }
  327. }
  328. const payPlatformOptionsList = ref([])
  329. // ==================== 导出相关逻辑 ====================
  330. const exportListVisible = ref(false)
  331. const exportList = ref([])
  332. const exportListLoading = ref(false)
  333. // 导出Excel
  334. const handleExport = async () => {
  335. const formatStatuses = (statuses) => {
  336. // 情况1:非数组/空值 → 返回空数组
  337. if (!Array.isArray(statuses)) {
  338. return [];
  339. }
  340. // 情况2:数组中包含 null 或 undefined → 返回空数组
  341. if (statuses.some(item => item === null || item === undefined)) {
  342. return [];
  343. }
  344. // 情况3:正常数组 → 返回原数组
  345. return statuses;
  346. };
  347. try {
  348. const params = {
  349. pageNum: queryParams.pageNum,
  350. pageSize: queryParams.pageSize,
  351. fundsDTO: {
  352. jwcode: queryParams.jwcode,
  353. localMarket: queryParams.markets,
  354. startTime: queryParams.timeRange?.[0] ? dayjs(queryParams.timeRange[0]).format('YYYY-MM-DD HH:mm:ss') : '',
  355. endTime: queryParams.timeRange?.[1] ? dayjs(queryParams.timeRange[1]).format('YYYY-MM-DD HH:mm:ss') : '',
  356. payType: normalizePayType(queryParams.payType || ''),
  357. orderCode: queryParams.orderCode,
  358. statuses: formatStatuses(queryParams.statuses),
  359. markets: [],
  360. }
  361. }
  362. // TODO: 确认导出接口 URL
  363. const res = await exportFunds(params)
  364. if (res.code == 200) {
  365. console.log('导出参数', params)
  366. ElMessage.success(t('elmessage.exportSuccess'))
  367. }
  368. } catch (error) {
  369. console.error(error)
  370. ElMessage.error(t('elmessage.exportError'))
  371. }
  372. }
  373. // 打开导出列表弹窗
  374. const openExportList = () => {
  375. getExportList()
  376. exportListVisible.value = true
  377. }
  378. // 获取导出列表
  379. const getExportList = async () => {
  380. exportListLoading.value = true
  381. try {
  382. const result = await request({ url: '/export/export' })
  383. if (result.code === 200) {
  384. const filteredData = result.data.filter(item => item.type == 15);
  385. exportList.value = filteredData || []
  386. } else {
  387. ElMessage.error(result.msg || t('elmessage.getExportListError'))
  388. }
  389. } catch (error) {
  390. console.error('获取导出列表出错:', error)
  391. ElMessage.error(t('elmessage.getExportListError'))
  392. } finally {
  393. exportListLoading.value = false
  394. }
  395. }
  396. // 下载导出文件
  397. const downloadExportFile = (item) => {
  398. if (item.state === 2) {
  399. const link = document.createElement('a')
  400. link.href = item.url
  401. link.download = item.fileName
  402. link.click()
  403. } else {
  404. ElMessage.warning(t('elmessage.exportingInProgress'))
  405. }
  406. }
  407. // 根据状态返回对应的标签类型
  408. const getTagType = (state) => {
  409. switch (state) {
  410. case 0: return 'info';
  411. case 1: return 'primary';
  412. case 2: return 'success';
  413. case 3: return 'danger';
  414. default: return 'info';
  415. }
  416. }
  417. // 根据状态返回对应的标签文案
  418. const getTagText = (state) => {
  419. switch (state) {
  420. case 0: return t('elmessage.pendingExecution');
  421. case 1: return t('elmessage.executing');
  422. case 2: return t('elmessage.executed');
  423. case 3: return t('elmessage.errorExecution');
  424. default: return t('elmessage.unknownStatus');
  425. }
  426. }
  427. const throttledsubmitRefund = _.throttle(handleRefund, 5000, {
  428. trailing: false
  429. })
  430. // 递归查找地区ID
  431. // normalizeMarketLabel 标准化地区名称,用于对比匹配
  432. const normalizeMarketLabel = (value) => {
  433. return String(value ?? '')
  434. .trim()
  435. .toLowerCase()
  436. .replace(/[\s_-]+/g, '')
  437. }
  438. // 传入的这两个参数对比,是否有匹配的地区ID
  439. const findValueByLabel = (options, label) => {
  440. // option和label都调用normalizeMarketLabel函数
  441. const normalizedLabel = normalizeMarketLabel(label)
  442. for (const option of options) {
  443. if (normalizeMarketLabel(option.label) === normalizedLabel) {
  444. return option.value
  445. }
  446. if (option.children && option.children.length) {
  447. const found = findValueByLabel(option.children, label)
  448. if (found) return found
  449. }
  450. }
  451. return null
  452. }
  453. //记录详情表单
  454. const formDataRow = ref({
  455. jwcode:'',
  456. marketName:'',
  457. goodsName:'',
  458. paymentCurrencyName:'',
  459. paymentAmount:'',
  460. payTime:'',
  461. voucher:'',
  462. name:'',
  463. activity:'',
  464. payType:'',
  465. receivedCurrencyName:'',
  466. receivedAmount:'',
  467. receivedTime:'',
  468. handlingCharge:'',
  469. remark:'',
  470. });
  471. //记录点击函数
  472. const showDetail=ref(false)
  473. const showRecordDetail = async (row) => {
  474. showDetail.value=true
  475. formDataRow.value={
  476. ...row,
  477. handlingCharge:row.handlingCharge || 0
  478. }
  479. }
  480. const paymentCurrency = ref([
  481. t('cash.currency.usd'), // 美元(USD)
  482. t('cash.currency.hkd'), // 港币(HKD)
  483. t('cash.currency.sgd'), // 新币(SGD)
  484. t('cash.currency.myr'), // 马币(MYR)
  485. t('cash.currency.thb'), // 泰铢(THB)
  486. t('cash.currency.cad'), // 加币(CAD)
  487. t('cash.currency.vnd'), // 越南盾(VND)
  488. t('cash.currency.krw'), // 韩元(KRW)
  489. t('cash.currency.rmb'), // 人民币(CNY)
  490. ])
  491. //新增流水
  492. const showAddDetail=ref(false)
  493. const type = ref('other')
  494. const addCashFlow=(s)=>{
  495. showAddDetail.value=true
  496. type.value=s
  497. }
  498. const selectAddType= (s) => {
  499. otherFormRef.value?.resetFields?.();
  500. ipay88FormRef.value?.resetFields?.();
  501. type.value=s
  502. }
  503. const otherFormRef = ref(null);
  504. const addOtherForm=ref({
  505. performanceMarket:"", //业绩归属地
  506. goodsName:"", //收入类别
  507. goodNum:"", //个数
  508. payType:"", //支付方式
  509. paymentCurrency:"", //付款币种
  510. paymentAmount:"", //付款金额
  511. payTime:"", //支付时间
  512. handlingCharge:"", //手续费
  513. remark:"", //备注
  514. isPerformance:'',
  515. submitterId: adminData.value?.id || '', // 直接初始化为用户ID
  516. submitterMarket: adminData.value?.markets || [] // 直接初始化为用户地区(通常是数组)
  517. })
  518. const otherRules = {
  519. performanceMarket: [
  520. { required: true, message: t('common.performanceByRegionPlaceholder'), trigger: 'change' }
  521. ],
  522. goodsName: [
  523. { required: true, message: t('cash.cashFlow.incomeCategoryPlaceholder'), trigger: 'change' }
  524. ],
  525. goodNum: [
  526. { type: 'number', message: t('cash.cashFlow.quantityMustBeNumber'), trigger: 'blur' }
  527. ],
  528. payType: [
  529. { required: true, message: t('elmessage.checkPayModel'), trigger: 'change' }
  530. ],
  531. paymentCurrency: [
  532. { required: true, message: t('common.payCurrencyPlaceholder'), trigger: 'change' }
  533. ],
  534. paymentAmount: [
  535. { required: true, message: t('common_add.payAmountPlaceholder'), trigger: 'blur' },
  536. { pattern: /^-?[0-9]+(\.[0-9]{1,2})?$/, message: t('cash.cashFlow.invalidFormat'), trigger: 'blur' }
  537. ],
  538. payTime: [
  539. { required: true, message: t('common_add.payTimePlaceholder'), trigger: 'change' }
  540. ],
  541. handlingCharge: [
  542. { pattern: /^[0-9]+(\.[0-9]{1,2})?$/, message: t('cash.cashFlow.invalidFormat'), trigger: 'blur' }
  543. ]
  544. };
  545. const handleOther=async ()=>{
  546. if (!otherFormRef.value) return;
  547. try {
  548. await otherFormRef.value.validate();
  549. if(addOtherForm.value.goodsName=== t('cash.cashFlow.localIntercompany') ||
  550. addOtherForm.value.goodsName=== t('cash.cashFlow.corporateIntercompany') ||
  551. addOtherForm.value.goodsName=== t('cash.cashFlow.otherIncomeNon') ){
  552. addOtherForm.value.isPerformance='0'
  553. }else{
  554. addOtherForm.value.isPerformance='1'
  555. }
  556. const submitData={
  557. performanceMarket:String(MarketNameForId(addOtherForm.value.performanceMarket)),
  558. goodsName:addOtherForm.value.goodsName,
  559. goodNum:String(addOtherForm.value.goodNum),
  560. payType:addOtherForm.value.payType,
  561. paymentCurrency:String(CurrencyForId(addOtherForm.value.paymentCurrency)),
  562. paymentAmount:addOtherForm.value.paymentAmount*100,
  563. payTime:addOtherForm.value.payTime,
  564. handlingCharge:addOtherForm.value.handlingCharge*100,
  565. remark:addOtherForm.value.remark,
  566. isPerformance:addOtherForm.value.isPerformance,
  567. submitterId: addOtherForm.value.submitterId,
  568. submitterMarket: addOtherForm.value.submitterMarket,
  569. }
  570. const handle =await request({
  571. url:'/cashCollection/addExFund',
  572. data:submitData
  573. })
  574. console.log('提交的数据:', submitData);
  575. if (handle.code == 200 || handle.status == 200) {
  576. ElMessage.success(t('elmessage.submitSuccess'));
  577. otherFormRef.value?.resetFields?.();
  578. showAddDetail.value = false;
  579. }
  580. } catch (error) {
  581. console.log('校验失败', error);
  582. }
  583. };
  584. const ipay88FormRef = ref(null);
  585. const addIpay88Form=ref({
  586. performanceMarket:"", //业绩归属地
  587. payType:"", //支付方式
  588. paymentCurrency:"", //付款币种
  589. handlingCharge:"", //手续费
  590. remark:"", //备注
  591. submitterId: adminData.value?.id || '', // 直接初始化为用户ID
  592. submitterMarket: adminData.value?.markets || [] // 直接初始化为用户地区(通常是数组)
  593. })
  594. const ipay88Rules = {
  595. performanceMarket: [{ required: true, message: t('common.performanceByRegionPlaceholder'), trigger: 'change' } ],
  596. goodsName: [{ required: true, message: t('cash.cashFlow.incomeCategoryPlaceholder'), trigger: 'change' }],
  597. payType: [ { required: true, message: t('common.payModelPlaceholder'), trigger: 'change' } ],
  598. paymentCurrency: [{ required: true, message: t('common.payCurrencyPlaceholder'), trigger: 'change' }],
  599. payTime: [{ required: true, message: t('common_add.payTimePlaceholder'), trigger: 'change' }],
  600. handlingCharge: [
  601. { required: true, message: t("common_add.feePlaceholder"),trigger: 'change'},
  602. { pattern: /^[0-9]+(\.[0-9]{1,2})?$/, message: t('cash.cashFlow.invalidFormat'),trigger: 'change' } ],
  603. remark: [ {required: true, max: 100, message: t('cash.cashFlow.remarksexceed'), trigger: 'change' }]
  604. };
  605. const handleIpay88 =async () =>{
  606. if (!ipay88FormRef.value) return;
  607. try {
  608. await ipay88FormRef.value.validate();
  609. const Data={
  610. performanceMarket:String(MarketNameForId(addIpay88Form.value.performanceMarket)),
  611. payType:addIpay88Form.value.payType,
  612. paymentCurrency:String(CurrencyForId(addIpay88Form.value.paymentCurrency)),
  613. handlingCharge:addIpay88Form.value.handlingCharge*100,
  614. remark:addIpay88Form.value.remark,
  615. submitterId: addIpay88Form.value.submitterId,
  616. submitterMarket: addIpay88Form.value.submitterMarket,
  617. }
  618. const handle =await request({
  619. url:'/cashCollection/addiPay88Fee',
  620. data:Data
  621. })
  622. console.log('提交的数据:', Data);
  623. if (handle.code == 200 || handle.status == 200) {
  624. ElMessage.success(t('elmessage.submitSuccess'));
  625. ipay88FormRef.value?.resetFields?.();
  626. showAddDetail.value = false;
  627. }
  628. } catch (error) {
  629. console.log('校验失败', error);
  630. }
  631. }
  632. // 修改 handleCancel 方法
  633. const handleCancel = () => {
  634. otherFormRef.value?.resetFields?.();
  635. ipay88FormRef.value?.resetFields?.();
  636. showAddDetail.value = false;
  637. };
  638. const previewVisible=ref(false)
  639. const handlePreviewClick=()=>{
  640. if(previewVisible.value){
  641. previewVisible.value=false
  642. }
  643. }
  644. onMounted(async () => {
  645. await initPermissions()
  646. await getMarket()
  647. await getPayPlatformOptions()
  648. // 处理从工作台跳转过来的地区参数
  649. // 如果出现URL中的?region=a&region=b 这种重复key,router会解析为['a','b'], 取第一个地区ID
  650. const regionName = Array.isArray(route.query.region) ? route.query.region[0] : route.query.region
  651. if (regionName && marketOptions.value.length) {
  652. const matchedId = findValueByLabel(marketOptions.value, regionName)
  653. if (matchedId) {
  654. // el-cascader 绑定的 markets 是数组
  655. queryParams.markets = [matchedId]
  656. }
  657. }
  658. fetchData()
  659. })
  660. </script>
  661. <template>
  662. <div class="cash-flow-container">
  663. <!-- 搜索区域 -->
  664. <el-card class="search-card">
  665. <div class="search-bar">
  666. <!-- 第一行 -->
  667. <div class="search-row">
  668. <div class="search-item">
  669. <span class="label">{{ t('common.jwcode') }}</span>
  670. <el-input v-model="queryParams.jwcode" :placeholder="t('common.jwcodePlaceholder')" clearable />
  671. </div>
  672. <div class="search-item">
  673. <span class="label">{{ t('cash.cashFlow.performanceMarket') }}</span>
  674. <el-select v-model="queryParams.performanceMarket"
  675. :placeholder="t('cash.cashFlow.performanceMarketPlaceholder')"
  676. clearable :multiple="true"
  677. style="width: 220px;"
  678. :prop="performanceMarket"
  679. collapse-tags >
  680. <el-option v-for="item in performanceMarket" :key="item" :label="item" :value="item" />
  681. </el-select>
  682. <!-- <span class="label">{{ t('common.performanceByRegion') }}</span> -->
  683. <!-- 下拉多选使用 el-cascader 匹配地区树结构 -->
  684. <!-- <el-cascader v-model="queryParams.markets" :options="marketOptions"
  685. :props="{ multiple: true, emitPath: false }" collapse-tags collapse-tags-tooltip
  686. :placeholder="t('common.performanceByRegionPlaceholder')" clearable style="width: 240px;" /> -->
  687. </div>
  688. <!-- 选择平台二级表单 -->
  689. <div class="search-item">
  690. <span class="label">{{ t('common.payPlatform1') }}</span>
  691. <el-cascader v-model="queryParams.platformSelection" :options="payPlatformOptionsList"
  692. :props="{ multiple: true ,value:'id',label:'name'}"
  693. collapse-tags collapse-tags-tooltip :placeholder="t('common.payPlatformPlaceholder1')" clearable
  694. style="width: 220px;"
  695. />
  696. </div>
  697. <div class="search-item">
  698. <span class="label">{{ t('common.orderNo') }}</span>
  699. <el-input v-model="queryParams.orderCode" :placeholder="t('common.orderNoPlaceholder')" clearable />
  700. </div>
  701. <div class="search-item" style="width: auto;">
  702. <span class="label">{{ t('common.payTime2') }}</span>
  703. <el-date-picker v-model="queryParams.timeRange" type="datetimerange" :range-separator="t('common.to')"
  704. :start-placeholder="t('common.startTime')" :end-placeholder="t('common.endTime')"
  705. :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" style="width: 350px;" />
  706. </div>
  707. <div class="search-btn-group">
  708. <el-button type="primary" @click="handleSearch">{{ t('common.search') }}</el-button>
  709. <el-button type="primary" @click="handleExport">{{ t('common.exportExcel') }}</el-button>
  710. <el-button type="primary" @click="openExportList">{{ t('common.viewExportList') }}</el-button>
  711. <el-button type="success" @click="handleReset">{{ t('common.reset') }}</el-button>
  712. </div>
  713. <div class="newAdd">
  714. <el-button v-if="hasNewTransactionFlow" class="newAdd_btn" @click="addCashFlow('other')">{{ t('common.addCashFlow') }}</el-button>
  715. </div>
  716. </div>
  717. </div>
  718. </el-card>
  719. <!-- 表格区域 -->
  720. <el-card class="table-card" >
  721. <el-table ref="tableRef" :data="tableData" v-loading="loading" style="width: 100%; flex: 1;"
  722. :cell-style="{ textAlign: 'center' }"
  723. :header-cell-style="{ background: '#F3FAFE', color: '#333', textAlign: 'center' }"
  724. @row-click="showRecordDetail" >
  725. <el-table-column type="index" :label="t('common_list.id')" width="60" align="center" fixed="left">
  726. <template #default="scope">
  727. <span>{{ scope.$index + 1 + (queryParams.pageNum - 1) * queryParams.pageSize }}</span>
  728. </template>
  729. </el-table-column>
  730. <el-table-column prop="payTime" :label="t('common_list.payTime2')" width="180" align="center" />
  731. <el-table-column prop="orderCode" :label="t('common_list.orderCode')" width="280" show-overflow-tooltip />
  732. <el-table-column prop="receivedMarket" :label="t('common_list.receiveArea')" width="280" show-overflow-tooltip >
  733. <template #default="{ row }">
  734. {{ marketFilter(row.receivedMarket) }}
  735. </template>
  736. </el-table-column>
  737. <el-table-column prop="performanceMarket" :label="t('common_list.performanceMarket')" width="120" show-overflow-tooltip >
  738. <template #default="{ row }">
  739. {{ marketFilter(row.performanceMarket) }}
  740. </template></el-table-column>
  741. <el-table-column prop="name" :label="t('common_list.name')" width="150" show-overflow-tooltip />
  742. <el-table-column prop="jwcode" :label="t('common_list.jwcode')" width="120" />
  743. <el-table-column prop="goodsName" :label="t('common_list.receiveType')" width="120" >
  744. <template #default="{ row }">{{ row.goodsName || '-' }}</template>
  745. </el-table-column>
  746. <el-table-column prop="remark" :label="t('common_list.remark')" width="120" />
  747. <el-table-column prop="goodNum" :label="t('common_list.nums')" width="120" />
  748. <el-table-column prop="payType" :label="t('common_list.paymentMethod')" width="120" />
  749. <el-table-column prop="receivedCurrencyName" :label="t('common_list.receiveCurrency')" width="120"
  750. show-overflow-tooltip />
  751. <el-table-column prop="paymentAmount" :label="t('common_list.payAmount')" width="150" align="right">
  752. </el-table-column>
  753. <el-table-column prop="handlingCharge" :label="t('common_list.fee')" width="100" align="right" >
  754. <template #default="{ row }">{{ row.handlingCharge || 0 }}</template>
  755. </el-table-column>
  756. <el-table-column prop="receivedAmount" :label="t('common_list.receiveAmount')" width="150" align="right">
  757. </el-table-column>
  758. <!-- <el-table-column :label="t('common_list.operation')" width="100" fixed="right" align="center">
  759. <template #default="{ row }">
  760. <el-button v-if="row.orderCode.slice(0, 4) == 'GOLD' && row.status === 4" type="danger" link size="small"
  761. @click="openRefundConfirm(row)">
  762. {{ t('common_list.refund') }}
  763. </el-button>
  764. </template>
  765. </el-table-column> -->
  766. </el-table>
  767. <!-- 分页 -->
  768. <div class="pagination-container">
  769. <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"
  770. :current-page="queryParams.pageNum" :page-size="queryParams.pageSize" :page-sizes="[10, 20, 50, 100]"
  771. @size-change="handlePageSizeChange" @current-change="handleCurrentChange" />
  772. </div>
  773. </el-card>
  774. <!-- 详情显示页 -->
  775. <el-dialog class="detailDialog" v-model="showDetail" :title="t('common_add.originalOrderInfo')" width="700px" destroy-on-close>
  776. <el-form :model="formDataRow" label-width="100px" class="detail-form" disabled>
  777. <div style="display: flex;">
  778. <div class="left">
  779. <div class="add-item">
  780. <el-form-item :label="t('common_list.jwcode')">
  781. <el-input v-model="formDataRow.jwcode" />
  782. </el-form-item>
  783. </div>
  784. <div class="add-item">
  785. <el-form-item :label="t('common_list.market')">
  786. <el-input v-model="formDataRow.marketName" />
  787. </el-form-item>
  788. </div>
  789. <div class="add-item">
  790. <el-form-item :label="t('common_list.productName')">
  791. <el-input v-model="formDataRow.goodsName" />
  792. </el-form-item>
  793. </div>
  794. <div class="add-item">
  795. <el-form-item :label="t('common_list.payCurrency')">
  796. <el-input v-model="formDataRow.paymentCurrencyName" />
  797. </el-form-item>
  798. </div>
  799. <div class="add-item">
  800. <el-form-item :label="t('common_list.payAmount')">
  801. <el-input v-model="formDataRow.paymentAmount" />
  802. </el-form-item>
  803. </div>
  804. <div class="add-item">
  805. <el-form-item :label="t('common_list.payTime2')">
  806. <el-input v-model="formDataRow.payTime" />
  807. </el-form-item>
  808. </div>
  809. <div class="add-item">
  810. <el-form-item :label="t('common_list.transferVoucher')">
  811. <div v-if="formDataRow.voucher" class="voucher-container">
  812. <el-image :src="formDataRow.voucher" :preview-src-list="[formDataRow.voucher]"
  813. fit="cover" class="voucher-img" v-model:preview-visible="previewVisible"
  814. @click="handlePreviewClick"/>
  815. </div>
  816. <div v-else class="no-voucher">{{ t('common_list.noTransferVoucher') }}</div>
  817. </el-form-item>
  818. </div>
  819. </div>
  820. <div class="right">
  821. <div class="add-item">
  822. <el-form-item :label="t('common_list.customerName')">
  823. <el-input v-model="formDataRow.name" />
  824. </el-form-item>
  825. </div>
  826. <div class="add-item">
  827. <el-form-item :label="t('common_list.activity')">
  828. <el-input v-model="formDataRow.activityName" />
  829. </el-form-item>
  830. </div>
  831. <div class="add-item">
  832. <el-form-item :label="t('common_list.payModel')">
  833. <el-input v-model="formDataRow.payType" />
  834. </el-form-item>
  835. </div>
  836. <div class="add-item">
  837. <el-form-item :label="t('common_add.receiveCurrency')">
  838. <el-input v-model="formDataRow.receivedCurrencyName" />
  839. </el-form-item>
  840. </div>
  841. <div class="add-item">
  842. <el-form-item :label="t('common_list.receiveAmount')">
  843. <el-input v-model="formDataRow.receivedAmount" />
  844. </el-form-item>
  845. </div>
  846. <div class="add-item">
  847. <el-form-item :label="t('common_list.receiveTime')">
  848. <el-input v-model="formDataRow.receivedTime" />
  849. </el-form-item>
  850. </div>
  851. <div class="add-item">
  852. <el-form-item :label="t('common_list.fee')">
  853. <el-input v-model="formDataRow.handlingCharge" />
  854. </el-form-item>
  855. </div>
  856. <div class="add-item">
  857. <el-form-item :label="t('common_list.submitter')">
  858. <el-input v-model="adminData.adminName" />
  859. </el-form-item>
  860. </div>
  861. <div class="add-item">
  862. <el-form-item :label="t('common_list.remark')">
  863. <el-input v-model="formDataRow.remark" type="textarea" :rows="2" />
  864. </el-form-item>
  865. </div>
  866. </div>
  867. </div>
  868. </el-form>
  869. <div style="display:flex;justify-content: center;margin-top: 5vh;" class="btnDiv">
  870. <el-button type="default" style="background-color: #7E91FF;" @click="showDetail = false">{{t('common.cancel')}}</el-button>
  871. <el-button v-show="formDataRow.orderCode.slice(0, 4) == 'GOLD' && formDataRow.status === 4 " type="primary" style="background-color: #2741DE; margin-left: 2.5vw;" @click="openRefundConfirm">{{ t('common.refund') }}</el-button>
  872. </div>
  873. </el-dialog>
  874. <!-- 新增流水页面 -->
  875. <el-dialog class="adddialog" v-model="showAddDetail" style="width: 400px;">
  876. <div style="width: fit-content; height: fit-content;margin-bottom: 20px; ">
  877. <el-button
  878. class="btnItem"
  879. :style="{backgroundColor: type === 'other' ? '#2741DE' : '#E5EBFE', color: type === 'other' ? 'white' : '#666' }"
  880. @click="selectAddType('other')" >{{ t('cash.cashFlow.otherIncome') }}</el-button>
  881. <el-button
  882. class="btnItem"
  883. :style="{ backgroundColor: type === 'ipay88' ? '#2741DE' : '#E5EBFE', color: type === 'ipay88' ? 'white' : '#666' }"
  884. @click="selectAddType('ipay88')" >{{ t('cash.cashFlow.addFee') }}</el-button>
  885. </div>
  886. <!-- 其他收入填写表单 -->
  887. <div v-if="type === 'other'" >
  888. <el-form :model="addOtherForm" :rules="otherRules" ref="otherFormRef" label-width="120px" label-position="left">
  889. <el-form-item :label=" t('cash.cashFlow.performanceMarket')" prop="performanceMarket">
  890. <el-select v-model="addOtherForm.performanceMarket" :placeholder="t('cash.cashFlow.performanceMarketPlaceholder')" >
  891. <el-option v-for="item in performanceMarket" :key="item" :label="item" :value="item" />
  892. </el-select>
  893. </el-form-item>
  894. <el-form-item :label="t('cash.cashFlow.incomeCategory')" prop="goodsName">
  895. <el-select v-model="addOtherForm.goodsName" :placeholder="t('cash.cashFlow.incomeCategoryPlaceholder')" >
  896. <el-option :value="t('cash.cashFlow.investmentIncome')" />
  897. <el-option :value="t('cash.cashFlow.taxRefund')" />
  898. <el-option :value="t('cash.cashFlow.governmentSubsidy')" />
  899. <el-option :value="t('cash.cashFlow.localIntercompany')" />
  900. <el-option :value="t('cash.cashFlow.corporateIntercompany')" />
  901. <el-option :value="t('cash.cashFlow.otherIncomeNon')" />
  902. <el-option :value="t('cash.cashFlow.otherIncomeYes')" />
  903. </el-select>
  904. </el-form-item>
  905. <el-form-item :label="t('cash.cashFlow.quantity')" prop="goodNum">
  906. <el-input v-model.number="addOtherForm.goodNum" :placeholder="t('cash.cashFlow.quantityPlaceholder')" />
  907. </el-form-item>
  908. <el-form-item :label="t('cash.cashFlow.payType')" prop="payType">
  909. <el-select v-model="addOtherForm.payType" :placeholder="t('cash.cashFlow.payTypePlaceholder')" >
  910. <el-option v-for="item in paytypeList" :key="item" :value="item" :label="item"/>
  911. </el-select>
  912. </el-form-item>
  913. <el-form-item :label="t('cash.cashFlow.paymentCurrency')" prop="paymentCurrency">
  914. <el-select v-model="addOtherForm.paymentCurrency" :placeholder="t('cash.cashFlow.paymentCurrencyPlaceholder')" >
  915. <el-option v-for="item in paymentCurrency" :key="item" :label="item" :value="item"/>
  916. </el-select>
  917. </el-form-item>
  918. <el-form-item :label="t('cash.cashFlow.paymentAmount')" prop="paymentAmount">
  919. <el-input v-model="addOtherForm.paymentAmount" :placeholder="t('cash.cashFlow.paymentAmountPlaceholder')" />
  920. </el-form-item>
  921. <el-form-item :label="t('cash.cashFlow.paymentTime')" prop="payTime">
  922. <el-date-picker v-model="addOtherForm.payTime" type="datetime" :placeholder="t('cash.cashFlow.paymentTimePlaceholder')" value-format="YYYY-MM-DD HH:mm:ss"/>
  923. </el-form-item>
  924. <el-form-item :label="t('cash.cashFlow.bankHandlingFee')" prop="handlingCharge">
  925. <el-input v-model="addOtherForm.handlingCharge" :placeholder="t('cash.cashFlow.bankHandlingFeePlaceholder')" />
  926. </el-form-item>
  927. <el-form-item :label="t('cash.cashFlow.remarks')" prop="remark">
  928. <el-input v-model="addOtherForm.remark" type="textarea" :rows="3" :placeholder="t('cash.cashFlow.remarksPlaceholder')" :maxlength="100" show-word-limit />
  929. </el-form-item>
  930. </el-form>
  931. <div class="btnDiv" >
  932. <el-button type="default" style="background-color: #7E91FF;" @click="handleCancel">{{t('cash.cashFlow.cancel')}}</el-button>
  933. <el-button type="primary" style="background-color: #2741DE; margin-left: 2.5vw;" @click="handleOther">{{t('cash.cashFlow.submit')}}</el-button>
  934. </div>
  935. </div>
  936. <!-- ipay88手续费填写表单 -->
  937. <div v-if="type === 'ipay88'" >
  938. <el-form :model="addIpay88Form" :rules="ipay88Rules" ref="ipay88FormRef" label-width="120px" label-position="left">
  939. <el-form-item :label="t('cash.cashFlow.payType')" prop="payType">
  940. <el-select v-model="addIpay88Form.payType" :placeholder="t('cash.cashFlow.payTypePlaceholder')" >
  941. <el-option :value="t('cash.cashFlow.ipay88')" :label="t('cash.cashFlow.ipay88')"/>
  942. <el-option :value="t('cash.cashFlow.cardPayment')" :label="t('cash.cashFlow.cardPayment')"/>
  943. </el-select>
  944. </el-form-item>
  945. <el-form-item :label="t('cash.cashFlow.performanceMarket')" prop="performanceMarket">
  946. <el-select v-model="addIpay88Form.performanceMarket" :placeholder="t('cash.cashFlow.performanceMarketPlaceholder')" >
  947. <el-option v-for="item in performanceMarket" :key="item" :label="item" :value="item" />
  948. </el-select>
  949. </el-form-item>
  950. <el-form-item :label="t('cash.cashFlow.incomeCategory')">
  951. <el-input disabled :value="t('cash.cashFlow.fixedProcessingFee')"></el-input>
  952. </el-form-item>
  953. <el-form-item :label="t('cash.cashFlow.settlementRegion')">
  954. <el-input disabled :value="t('cash.cashFlow.Malaysia')"></el-input>
  955. </el-form-item>
  956. <el-form-item :label="t('cash.cashFlow.paymentCurrency')" prop="paymentCurrency">
  957. <el-select v-model="addIpay88Form.paymentCurrency" :placeholder="t('cash.cashFlow.paymentCurrencyPlaceholder')">
  958. <el-option v-for="item in paymentCurrency" :key="item" :label="item" :value="item"/>
  959. </el-select>
  960. </el-form-item>
  961. <el-form-item :label="t('cash.cashFlow.paymentAmount')" prop="paymentAmount">
  962. <el-input disabled v-model="addIpay88Form.paymentAmount" :placeholder="t('cash.cashFlow.paymentAmountPlaceholder')" value="0"/>
  963. </el-form-item>
  964. <el-form-item :label="t('cash.cashFlow.processingFee')" prop="handlingCharge">
  965. <el-input v-model="addIpay88Form.handlingCharge" :placeholder="t('cash.cashFlow.processingFeePlaceholder')" />
  966. </el-form-item>
  967. <el-form-item :label="t('cash.cashFlow.remarks')" prop="remark">
  968. <el-input v-model="addIpay88Form.remark" type="textarea" :rows="3" :placeholder="t('cash.cashFlow.remarkPlaceholderDate')" :maxlength="100" show-word-limit/>
  969. </el-form-item>
  970. </el-form>
  971. <div class="btnDiv">
  972. <el-button type="default" style="background-color: #7E91FF;" @click="handleCancel">{{t('cash.cashFlow.cancel')}}</el-button>
  973. <el-button type="primary" style="background-color: #2741DE; margin-left: 2.5vw;" @click="handleIpay88">{{t('cash.cashFlow.submit')}}</el-button>
  974. </div>
  975. </div>
  976. </el-dialog>
  977. <!-- 导出列表弹窗 -->
  978. <el-dialog v-model="exportListVisible" :title="t('common_export.exportList')" width="80%">
  979. <el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
  980. <el-table-column prop="fileName" :label="t('common_export.fileName')" />
  981. <el-table-column prop="state" :label="t('common_export.status')">
  982. <template #default="scope">
  983. <el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
  984. {{ getTagText(scope.row.state) }}
  985. </el-tag>
  986. </template>
  987. </el-table-column>
  988. <el-table-column prop="createTime" :label="t('common_export.createTime')">
  989. <template #default="scope">
  990. {{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
  991. </template>
  992. </el-table-column>
  993. <el-table-column :label="t('common_export.operation')">
  994. <template #default="scope">
  995. <el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
  996. :disabled="scope.row.state !== 2">
  997. {{ t('common_export.download') }}
  998. </el-button>
  999. </template>
  1000. </el-table-column>
  1001. </el-table>
  1002. <template #footer>
  1003. <div class="dialog-footer">
  1004. <el-button text @click="exportListVisible = false">{{ t('common_export.close') }}</el-button>
  1005. </div>
  1006. </template>
  1007. </el-dialog>
  1008. <div class="recallDialog" v-show="refundConfirmDialog">
  1009. <div class="close">
  1010. <button @click="closeConfirmRefund" class="Btn">{{ t('common.close') }}</button>
  1011. </div>
  1012. <div class="text">
  1013. <text class="txt">{{ textContent }}</text>
  1014. </div>
  1015. <div class="cancle">
  1016. <button @click="closeConfirmRefund" class="Btn">{{ t('common.cancel') }}</button>
  1017. </div>
  1018. <div class="confirm">
  1019. <button @click="openRefundDialog" class="Btn">{{ t('common.confirm') }}</button>
  1020. </div>
  1021. </div>
  1022. <el-dialog v-model="refundDialog" :title="t('common_add.refund')" class="refundDialog" overflow draggable
  1023. style="width: 40vw;" :before-close="closeRefundForm">
  1024. <div style="display: flex;">
  1025. <div class="left">
  1026. <div class="add-item">
  1027. <el-text style="width:4vw;">{{ t('common_add.jwcode') }}</el-text>
  1028. <el-input v-model="refundFormData.jwcode" style="width:10vw;" disabled />
  1029. </div>
  1030. <div class="add-item">
  1031. <el-text style="width:4vw;">{{ t('common_add.customerName') }}</el-text>
  1032. <el-input v-model="refundFormData.name" style="width:10vw;" disabled />
  1033. </div>
  1034. <div class="add-item">
  1035. <el-text style="width:4vw;">{{ t('common_add.market') }}</el-text>
  1036. <el-input v-model="refundFormData.marketName" style="width:10vw;" disabled />
  1037. </div>
  1038. <div class="add-item">
  1039. <el-text style="width:4vw;">{{ t('common_add.activity') }}</el-text>
  1040. <el-input v-model="refundFormData.activityName" style="width:10vw;" disabled />
  1041. </div>
  1042. <div class="add-item">
  1043. <el-text style="width:4vw;">{{ t('common_add.productName') }}</el-text>
  1044. <el-input v-model="refundFormData.goodsName" style="width:10vw;" disabled />
  1045. </div>
  1046. <div style="display: flex; margin-bottom: 10px;">
  1047. <div style=" display: flex; align-items: center;justify-content: center; ">
  1048. <span style="color: #999999; white-space: nowrap;">{{ t('common_add.permanentGold')
  1049. }}</span>
  1050. <el-input style="padding-right: 10px; height: 30px; width: 70px;"
  1051. v-model="refundFormData.oldpermanentGold" disabled />
  1052. </div>
  1053. <div style=" display: flex; align-items: center;justify-content: center; ">
  1054. <span style="color: #999999; white-space: nowrap;">{{ t('common_add.freeGold') }}</span>
  1055. <el-input style="padding-right: 10px; height: 30px; width: 70px;" v-model="refundFormData.oldfreeGold"
  1056. disabled />
  1057. </div>
  1058. </div>
  1059. <div class="add-item">
  1060. <el-text style="width:4vw;">{{ t('common_add.payCurrency') }}</el-text>
  1061. <el-input v-model="refundFormData.paymentCurrency" style="width:10vw;" disabled />
  1062. </div>
  1063. <div class="add-item">
  1064. <el-text style="width:4vw;">{{ t('common_add.payAmount') }}</el-text>
  1065. <el-input v-model="refundFormData.paymentAmount" style="width:10vw;" disabled />
  1066. </div>
  1067. <div class="add-item">
  1068. <el-text style="width:4vw;">{{ t('common_add.payMethod') }}</el-text>
  1069. <el-input v-model="refundFormData.payType" style="width:10vw;" disabled />
  1070. </div>
  1071. <div class="add-item">
  1072. <el-text style="width:4vw;">{{ t('common_add.payTime') }}</el-text>
  1073. <el-date-picker v-model="refundFormData.payTime" type="datetime" style="width:10vw;" disabled />
  1074. </div>
  1075. <div class="add-item">
  1076. <el-text style="width:4vw;" size="small">{{ t('common_add.transferVoucher') }}</el-text>
  1077. <el-form-item :rules="{ required: true, message: t('common_add.uploadPhoto'), trigger: 'change' }">
  1078. <el-upload ref="uploadRef" :auto-upload="false" list-type="picture-card" :show-file-list="false">
  1079. <template #default>
  1080. <img v-if="refundFormData.voucher" :src="refundFormData.voucher"
  1081. style="width: 100%; height: 100%; object-fit: cover;">
  1082. <el-icon v-else>
  1083. <Plus />
  1084. </el-icon>
  1085. </template>
  1086. </el-upload>
  1087. </el-form-item>
  1088. </div>
  1089. <div class="add-item">
  1090. <el-text style="width:4vw;">{{ t('common_add.remark') }}</el-text>
  1091. <el-input v-model="refundFormData.remark" style="width:10vw;" :rows="2" type="textarea" maxLength="100"
  1092. disabled show-word-limit />
  1093. </div>
  1094. </div>
  1095. <div class="right">
  1096. <div class="add-item">
  1097. <el-text style="width:4vw;">{{ t('common_add.refundModel') }}</el-text>
  1098. <el-radio-group v-model="refundFormData.refundModel">
  1099. <el-radio value="0">{{ t('common_add.refundModelAll') }}</el-radio>
  1100. <el-radio value="1">{{ t('common_add.refundModelPart') }}</el-radio>
  1101. </el-radio-group>
  1102. </div>
  1103. <div v-show="refundFormData.refundModel == '1'" style="display: flex; margin-bottom: 10px;">
  1104. <div style=" display: flex; align-items: center;justify-content: center; ">
  1105. <span style="color: #999999; white-space: nowrap;">{{ t('common_add.permanentGold')
  1106. }}</span>
  1107. <el-input style="padding-right: 10px; height: 30px; width: 70px;"
  1108. v-model="refundFormData.permanentGold" />
  1109. </div>
  1110. <div style=" display: flex; align-items: center;justify-content: center; ">
  1111. <span style="color: #999999; white-space: nowrap;">{{ t('common_add.freeGold') }}</span>
  1112. <el-input style="padding-right: 10px; height: 30px; width: 70px;" v-model="refundFormData.freeGold" />
  1113. </div>
  1114. </div>
  1115. <div class="add-item">
  1116. <el-text style="width:4vw;">{{ t('common_add.refundReason') }}</el-text>
  1117. <el-input v-model="refundFormData.refundReason" style="width:10vw;" :rows="5" maxlength="150"
  1118. show-word-limit type="textarea" />
  1119. </div>
  1120. <div>{{ t('common_add.tip') }}</div>
  1121. <div style="display:flex;justify-content: center;margin-top: 5vh;">
  1122. <el-button type="default" @click="resetRefund">{{ t('common.reset') }}</el-button>
  1123. <el-button type="primary" @click="throttledsubmitRefund">{{ t('common.submit') }}</el-button>
  1124. </div>
  1125. </div>
  1126. </div>
  1127. </el-dialog>
  1128. </div>
  1129. </template>
  1130. <style lang="scss">
  1131. .refund-popover {
  1132. background-color: #EEF5FE !important;
  1133. border: none !important;
  1134. padding: 12px !important;
  1135. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  1136. width: 100px;
  1137. min-width: none;
  1138. .el-popper__arrow::before {
  1139. background-color: #EEF5FE !important;
  1140. border-color: #EEF5FE !important;
  1141. }
  1142. }
  1143. </style>
  1144. <style scoped lang="scss">
  1145. :deep(.detailDialog) {
  1146. background: #F3FAFE !important;
  1147. .left {
  1148. width: 50%;
  1149. height: 60vh;
  1150. min-height: 400px;
  1151. .add-item {
  1152. display: flex;
  1153. align-items: center;
  1154. margin-bottom: 1vh;
  1155. }
  1156. .image {
  1157. width: 4vw !important;
  1158. height: 4vw !important;
  1159. }
  1160. }
  1161. .right {
  1162. width: 50%;
  1163. height: 60vh;
  1164. .add-item {
  1165. display: flex;
  1166. align-items: center;
  1167. margin-bottom: 1vh;
  1168. }
  1169. }
  1170. .voucher-img {
  1171. width: 100px; /* 限制宽度 */
  1172. height: 100px; /* 限制高度 */
  1173. border-radius: 4px;
  1174. cursor: pointer; /* 鼠标放上去显示手型,提示可点击预览 */
  1175. }
  1176. .no-voucher {
  1177. color: #909399;
  1178. font-size: 14px;
  1179. }
  1180. }
  1181. :deep(.adddialog .el-form-item__label) {
  1182. min-width: 120px;
  1183. width: auto;
  1184. font-weight: 800;
  1185. padding-bottom: 15px;
  1186. }
  1187. .btnItem {
  1188. margin-left: 10px;
  1189. border-radius: 5px;
  1190. }
  1191. .btnDiv{
  1192. text-align: center;
  1193. margin-top: 30px;
  1194. }
  1195. :deep(.adddialog) {
  1196. min-width: 400px;
  1197. background-color: #F3FAFE !important;
  1198. margin-top: 8vh;
  1199. border-radius: 8px;
  1200. }
  1201. .popover-content {
  1202. .popover-title {
  1203. color: #409EFF;
  1204. font-weight: bold;
  1205. font-size: 14px;
  1206. margin-bottom: 8px;
  1207. }
  1208. .popover-item {
  1209. display: flex;
  1210. font-size: 13px;
  1211. color: #606266;
  1212. margin-bottom: 4px;
  1213. &:last-child {
  1214. margin-bottom: 0;
  1215. }
  1216. .label {
  1217. color: #606266;
  1218. }
  1219. .value {
  1220. color: #606266;
  1221. margin-left: 4px;
  1222. }
  1223. }
  1224. }
  1225. .cash-flow-container {
  1226. display: flex;
  1227. flex-direction: column;
  1228. height: 100%;
  1229. }
  1230. .search-card {
  1231. margin-bottom: 10px;
  1232. background: #F3FAFE; // 浅蓝背景
  1233. border: none;
  1234. :deep(.el-card__body) {
  1235. padding: 15px;
  1236. }
  1237. }
  1238. .search-bar {
  1239. display: flex;
  1240. flex-direction: column;
  1241. gap: 15px;
  1242. }
  1243. .search-row {
  1244. display: flex;
  1245. flex-wrap: wrap;
  1246. gap: 20px;
  1247. align-items: center;
  1248. }
  1249. .search-item {
  1250. display: flex;
  1251. align-items: center;
  1252. .label {
  1253. font-size: 15px; // 参考 coinConsumeDetail 的 .text size="large"
  1254. color: #000; // 或 #606266
  1255. white-space: nowrap;
  1256. margin-right: 8px;
  1257. min-width: 60px;
  1258. text-align: right;
  1259. }
  1260. .el-input,
  1261. .el-select {
  1262. width: 200px;
  1263. }
  1264. }
  1265. .search-btn-group {
  1266. margin-left: 20px; // 靠右对齐
  1267. display: flex;
  1268. gap: 10px;
  1269. }
  1270. .newAdd {
  1271. margin-left: auto;
  1272. display: flex;
  1273. width: 240px;
  1274. justify-content: flex-end;
  1275. }
  1276. .newAdd_btn{
  1277. background-color: blueviolet;
  1278. color: white;
  1279. }
  1280. .table-card {
  1281. background: #E7F4FD;
  1282. flex: 1;
  1283. border: none;
  1284. display: flex;
  1285. flex-direction: column;
  1286. :deep(.el-card__body) {
  1287. padding: 20px;
  1288. flex: 1;
  1289. display: flex;
  1290. flex-direction: column;
  1291. overflow: hidden;
  1292. }
  1293. }
  1294. .pagination-container {
  1295. margin-top: 15px;
  1296. display: flex;
  1297. justify-content: flex-start;
  1298. }
  1299. // 表格样式覆盖 (参考 coinConsumeDetail)
  1300. :deep(.el-table__header-wrapper),
  1301. :deep(.el-table__body-wrapper),
  1302. :deep(.el-table__cell),
  1303. :deep(.el-table__body td) {
  1304. background-color: #F3FAFE !important;
  1305. }
  1306. :deep(.el-table__row:hover > .el-table__cell) {
  1307. background-color: #E5EBFE !important;
  1308. }
  1309. .refundDialog {
  1310. .left {
  1311. width: 50%;
  1312. height: 70vh;
  1313. min-height: 700px;
  1314. padding: 0 2vw;
  1315. .add-item {
  1316. display: flex;
  1317. align-items: center;
  1318. margin-bottom: 1vh;
  1319. }
  1320. .image {
  1321. width: 4vw !important;
  1322. height: 4vw !important;
  1323. }
  1324. }
  1325. .right {
  1326. width: 50%;
  1327. height: 50vh;
  1328. .add-item {
  1329. display: flex;
  1330. align-items: center;
  1331. margin-bottom: 1vh;
  1332. }
  1333. }
  1334. }
  1335. .recallDialog {
  1336. //撤回弹窗提示
  1337. height: 392px;
  1338. width: 700px;
  1339. background-image: url('/src/assets/receive-recall.png');
  1340. position: fixed; // 固定定位,相对于浏览器窗口
  1341. top: 50%; // 距离顶部50%
  1342. left: 50%; // 距离左侧50%
  1343. transform: translate(-50%, -50%); // 向左、向上平移自身宽高的50%,实现居中
  1344. z-index: 1000; // 确保在其他元素上层显示
  1345. .close {
  1346. position: absolute;
  1347. left: 625px;
  1348. top: 20px;
  1349. height: 38px;
  1350. width: 38px;
  1351. opacity: 0;
  1352. .Btn {
  1353. height: 100%;
  1354. width: 100%;
  1355. border-radius: 10px;
  1356. }
  1357. }
  1358. .text {
  1359. position: absolute;
  1360. left: 185px;
  1361. top: 190px;
  1362. height: 67px;
  1363. width: 500px;
  1364. .txt {
  1365. height: 100%;
  1366. width: 100%;
  1367. color: #001a42;
  1368. font-family: "PingFang SC";
  1369. font-size: 38px;
  1370. font-style: normal;
  1371. font-weight: 900;
  1372. line-height: normal;
  1373. }
  1374. }
  1375. .cancle {
  1376. position: absolute;
  1377. left: 185px;
  1378. top: 304px;
  1379. height: 55px;
  1380. width: 150px;
  1381. opacity: 0;
  1382. .Btn {
  1383. height: 100%;
  1384. width: 100%;
  1385. border-radius: 20px;
  1386. }
  1387. }
  1388. .confirm {
  1389. position: absolute;
  1390. left: 375px;
  1391. top: 304px;
  1392. height: 55px;
  1393. width: 150px;
  1394. opacity: 0;
  1395. .Btn {
  1396. height: 100%;
  1397. width: 100%;
  1398. border-radius: 20px;
  1399. }
  1400. }
  1401. }
  1402. </style>