report.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
  3. <cwg-header :title="t('Home.page_ib.item3')" />
  4. <view class="info-card">
  5. <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
  6. @reset="handleReset" />
  7. <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="queryParams" :api="listApi"
  8. :show-operation="false" :showSummary="true" :summaryMethod="getSummaries">
  9. <template #equity="{ row }">
  10. <view class="equity-cell">
  11. <view class="start-equity">{{ formatNumberAll(row.startEquity || '0') }}</view>
  12. <view class="end-equity">{{ formatNumberAll(row.endEquity || '0') }}</view>
  13. </view>
  14. </template>
  15. </cwg-tabel>
  16. </view>
  17. </cwg-page-wrapper>
  18. </template>
  19. <script setup lang="ts">
  20. import { computed, ref, nextTick, onMounted } from 'vue';
  21. import { useI18n } from 'vue-i18n';
  22. import { documentaryApi } from '@/service/documentary';
  23. import { ibApi } from '@/service/ib';
  24. const { t } = useI18n();
  25. // 搜索参数
  26. const searchParams = ref<any>({
  27. type: 1,
  28. agentId: '',
  29. followLogin: '',
  30. dealLogin: '',
  31. date: []
  32. });
  33. const agentLevels = ref<Array<{ key: string, options: any[] }>>([
  34. {
  35. key: 'agentId_0',
  36. options: [
  37. { value: 0, text: t('news_add_field.IbReport.ALL') },
  38. { value: -1, text: t('news_add_field.IbReport.DirectlyUnder') },
  39. ],
  40. },
  41. ]);
  42. const filterFields = ref<any[]>([]);
  43. // 处理传给表格的参数
  44. const queryParams = computed(() => {
  45. let finalAgentId = searchParams.value.agentId_0 || '';
  46. for (let i = agentLevels.value.length - 1; i >= 0; i--) {
  47. const val = searchParams.value[`agentId_${i}`];
  48. if (val !== undefined && val !== '') {
  49. finalAgentId = val;
  50. break;
  51. }
  52. }
  53. return {
  54. type: searchParams.value.type,
  55. agentId: finalAgentId || '',
  56. followLogin: searchParams.value.followLogin || '',
  57. dealLogin: searchParams.value.dealLogin || '',
  58. date: searchParams.value.date || []
  59. };
  60. });
  61. const handleAgentChange = async (val: any, levelIndex: number) => {
  62. // 截断当前层级之后的所有层级
  63. agentLevels.value.splice(levelIndex + 1);
  64. // 清理 searchParams 中被移除层级的值
  65. for (let key in searchParams.value) {
  66. if (key.startsWith('agentId_')) {
  67. const idx = parseInt(key.split('_')[1]);
  68. if (idx > levelIndex) {
  69. delete searchParams.value[key];
  70. }
  71. }
  72. }
  73. handleSearch();
  74. if (val === 0 || val === -1 || val === '') {
  75. return;
  76. }
  77. try {
  78. const res = await ibApi.ibTree({ pid: val });
  79. if (res.code === 200 && res.data && res.data.length > 0) {
  80. const nextOptions: any[] = [];
  81. res.data.forEach((item: any) => {
  82. if (item.ibNo) {
  83. nextOptions.push({
  84. value: item.id,
  85. text: item.name ? `${item.ibNo} - ${item.name}` : item.ibNo,
  86. });
  87. }
  88. });
  89. if (nextOptions.length > 0) {
  90. agentLevels.value.push({
  91. key: `agentId_${levelIndex + 1}`,
  92. options: nextOptions,
  93. });
  94. }
  95. }
  96. setFields();
  97. } catch (error) {
  98. console.error(error);
  99. }
  100. };
  101. const searchIbTree = async () => {
  102. try {
  103. const res = await ibApi.ibTree({ pid: 0 });
  104. if (res.code === 200 && res.data) {
  105. res.data.forEach((item: any) => {
  106. if (item.ibNo) {
  107. agentLevels.value[0].options.push({
  108. value: item.id,
  109. text: item.name ? `${item.ibNo} - ${item.name}` : item.ibNo,
  110. });
  111. }
  112. });
  113. }
  114. } catch (e) {
  115. console.error(e);
  116. }
  117. };
  118. const setFields = () => {
  119. const fields: any[] = [
  120. { key: 'dealLogin', type: 'input', placeholder: t('Documentary.TundManagement.item11') },
  121. { key: 'followLogin', type: 'input', placeholder: t('Documentary.console.item28') },
  122. ];
  123. agentLevels.value.forEach((level, index) => {
  124. fields.push({
  125. key: level.key,
  126. type: 'select',
  127. label: t('State.All'),
  128. options: level.options,
  129. placeholder: t('State.All'),
  130. onChange: (val: any) => handleAgentChange(val, index),
  131. defaultValue: index === 0 ? 0 : '',
  132. });
  133. });
  134. fields.push({ key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' });
  135. filterFields.value = fields;
  136. };
  137. onMounted(async () => {
  138. await searchIbTree();
  139. setFields();
  140. });
  141. const tableRef = ref();
  142. const handleSearch = () => {
  143. nextTick(() => {
  144. tableRef.value?.refreshTable();
  145. });
  146. };
  147. const handleReset = () => {
  148. agentLevels.value.splice(1);
  149. searchParams.value = {
  150. type: 1,
  151. agentId: '',
  152. followLogin: '',
  153. dealLogin: '',
  154. date: []
  155. };
  156. setFields();
  157. nextTick(() => {
  158. tableRef.value?.refreshTable();
  159. });
  160. };
  161. // 工具方法
  162. const NumberDesensitization = (value: any) => {
  163. if (!value) return '--';
  164. const str = String(value);
  165. if (str.length > 4) {
  166. return str.substring(0, 2) + '***' + str.substring(str.length - 2);
  167. }
  168. return str;
  169. };
  170. const formatNumberAll = (value: any) => {
  171. if (value === "***") return "***";
  172. if (isNaN(value)) return '0';
  173. let value1 = value.toString();
  174. const isNegative = value1.indexOf("-") > -1;
  175. if (isNegative) value1 = value1.split("-")[1];
  176. let num = value1.split(".");
  177. let interCount = num[0].length;
  178. if (interCount < 3) {
  179. return isNegative ? "-" + value1 : value1;
  180. }
  181. let index = 0;
  182. let inter = "";
  183. for (let i = interCount - 3; i >= 0; i -= 3) {
  184. inter = num[0].substr(i, 3) + (inter == "" ? "" : ",") + inter;
  185. index = i;
  186. }
  187. if (index > 0) {
  188. inter = num[0].substr(0, index) + (inter == "" ? "" : ",") + inter;
  189. }
  190. const formatted = inter + (num.length == 1 ? "" : "." + num[1]);
  191. return isNegative ? "-" + formatted : formatted;
  192. };
  193. const groupCurrency1 = (type: string) => {
  194. if (type === "GBP") return "£";
  195. if (type === "USD") return "$";
  196. if (type === "EUR") return "€";
  197. if (type === "USC") return "¢";
  198. return "$";
  199. };
  200. const currentColumns = computed(() => [
  201. {
  202. prop: 'dealLogin',
  203. label: t('Documentary.tradingCenter.item18'),
  204. align: 'left',
  205. formatter: ({ row }: any) => NumberDesensitization(row.dealLogin)
  206. },
  207. {
  208. prop: 'followLogin',
  209. label: t('Documentary.console.item28'),
  210. align: 'left',
  211. formatter: ({ row }: any) => row.followLogin || '--'
  212. },
  213. {
  214. prop: 'dealCommission',
  215. label: t('Documentary.Report.item4'),
  216. align: 'left',
  217. formatter: ({ row }: any) => formatNumberAll(row.dealCommission || '0')
  218. },
  219. {
  220. prop: 'dealRatio',
  221. label: t('Documentary.Report.item5'),
  222. align: 'left',
  223. formatter: ({ row }: any) => row.dealRatio || '0%'
  224. },
  225. {
  226. prop: 'agentCommission',
  227. label: t('Documentary.Report.item8'),
  228. align: 'left',
  229. formatter: ({ row }: any) => `${groupCurrency1(row.currency)}${formatNumberAll(row.agentCommission || '0')}`
  230. },
  231. {
  232. prop: 'agentRatio',
  233. label: t('Documentary.Report.item7'),
  234. align: 'left',
  235. formatter: ({ row }: any) => row.agentRatio || '0%'
  236. },
  237. {
  238. prop: 'equity',
  239. label: t('Documentary.Report.item17') + ' / ' + t('Documentary.Report.item18'),
  240. align: 'left',
  241. slot: 'equity'
  242. }
  243. ]);
  244. const getSummaries = (param: any) => {
  245. const { columns, summaryData } = param;
  246. const sums: string[] = [];
  247. if (!summaryData || Object.keys(summaryData).length === 0) return sums;
  248. columns.forEach((column: any, index: number) => {
  249. if (index === 0) {
  250. sums[index] = t('Label.Total');
  251. return;
  252. }
  253. const prop = column.prop;
  254. if (!prop) {
  255. sums[index] = '';
  256. return;
  257. }
  258. if (['dealCommission', 'agentCommission'].includes(prop)) {
  259. sums[index] = summaryData[prop] ? formatNumberAll(summaryData[prop]) : '0';
  260. } else {
  261. sums[index] = '';
  262. }
  263. });
  264. return sums;
  265. };
  266. const listApi = ref(documentaryApi.followReportCommission);
  267. </script>
  268. <style scoped lang="scss">
  269. @import "@/uni.scss";
  270. .equity-cell {
  271. display: flex;
  272. flex-direction: column;
  273. align-items: flex-start;
  274. gap: px2rpx(4);
  275. .start-equity, .end-equity {
  276. font-size: px2rpx(14);
  277. line-height: 1.2;
  278. }
  279. .end-equity {
  280. color: var(--color-slate-500);
  281. }
  282. }
  283. </style>