linkList.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. <template>
  2. <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
  3. <cwg-header :title="t('Ib.Index.CreateLink')" />
  4. <view class="account-section">
  5. <view class="add-link" @click="addLink">
  6. <view class="add-link-content" @click="addLink">
  7. <cwg-icon name="icon_add" :size="18" color="#fff"></cwg-icon>
  8. {{ t('Ib.Index.CreateLink') }}
  9. </view>
  10. </view>
  11. <cwg-tabel ref="tableRef" :columns="columns" :mobilePrimaryFields="mobilePrimaryFields"
  12. :api="listApi" :show-operation="false" :showPagination="true" isPages @goPages="openDetail">
  13. <template #link="{ row }">
  14. <view class="link-cell">
  15. <uni-easyinput class="read-input" disabled v-model.trim="row.link"></uni-easyinput>
  16. <view class="qr-code-icon" @click.stop="showQrCodeDialog(row.link)">
  17. <svg style="width: 18px; height: 18px; fill: #409eff; vertical-align: middle;" viewBox="0 0 24 24"
  18. xmlns="http://www.w3.org/2000/svg">
  19. <path
  20. d="M3 3h8v8H3V3zm2 2v4h4V5H5zm8-2h8v8h-8V3zm2 2v4h4V5h-4zM3 13h8v8H3v-8zm2 2v4h4v-4H5zm13-2h3v2h-3v-2zm0 4h3v2h-3v-2zm-4-4h2v6h-2v-6zm4-8h2v2h-2V3zm0 4h2v2h-2V7zm-4 0h2v2h-2V7z" />
  21. </svg>
  22. </view>
  23. <view class="copy" @click.stop="CopyLink(row.link)">
  24. {{ t('Ib.Index.Copy') }}
  25. </view>
  26. </view>
  27. </template>
  28. <template #linkValue="{ row }">
  29. <view class="link-cell">
  30. <uni-easyinput class="read-input" disabled v-model.trim="row.linkValue"></uni-easyinput>
  31. <view class="copy" @click.stop="CopyLink(row.linkValue)">
  32. {{ t('Ib.Index.Copy') }}
  33. </view>
  34. </view>
  35. </template>
  36. </cwg-tabel>
  37. <!--详情-->
  38. <link-detail-dialog :visible="datailVisible" @close="closeDetail" :detail="detail" @showQrCode="showQrCodeDialog"/>
  39. <cwg-popup ref="linkPopup" :visible="dialogLink" type="center" :title="t('Ib.Index.CreateLink')" showFooterLine @close="dialogLink = false" @confirm="saveLink">
  40. <view class="dia-content">
  41. <view class="content" style="font-size: 14px; text-align: left">
  42. <view class="label">{{ t('Ib.Custom.NameLabelColon') }} </view>
  43. <uni-easyinput
  44. v-model="linkName"
  45. :placeholder="t('Ib.Custom.NameLabel')"
  46. />
  47. <view class="label-tit">{{ t('Ib.Index.Spread5') }} :</view>
  48. <!--标准账户-->
  49. <view class="label">{{ t('AccountType.StandardAccount') }} :</view>
  50. <cwg-combox
  51. v-model:value="accountTypeSettings.standard.selectedIndex"
  52. :options="getAvailableSpreads('7')"
  53. @change="(val)=>handleAccountTypeChange('standard', '7',val)"
  54. :placeholder="t('placeholder.choose')"
  55. />
  56. <view class="label">{{ t('AccountType.SeniorAccount') }}</view>
  57. <cwg-combox
  58. v-model:value="accountTypeSettings.ecn.selectedIndex"
  59. :options="getAvailableSpreads('2')"
  60. @change="(val)=>handleAccountTypeChange('ecn', '2',val)"
  61. :placeholder="t('placeholder.choose')"
  62. />
  63. <view class="label">{{ t('AccountType.CentAccount') }}</view>
  64. <cwg-combox
  65. v-model:value="accountTypeSettings.cent.selectedIndex"
  66. :options="getAvailableSpreads('8')"
  67. @change="(val)=>handleAccountTypeChange('cent', '8',val)"
  68. :placeholder="t('placeholder.choose')"
  69. />
  70. <view class="btn">
  71. <button
  72. class="crm-cursor"
  73. @click="CreateLink"
  74. >{{ t('Ib.Index.CreateLink') }}
  75. </button>
  76. </view>
  77. <view class="link qrCode" v-if="link">
  78. <QrCode
  79. ref="qrCode"
  80. :text="link"
  81. :width="200"
  82. :height="200"
  83. />
  84. <view class="btn">
  85. <button
  86. class="crm-cursor"
  87. @click="downloadQrCode()"
  88. >{{ t('Btn.item9') }}
  89. </button>
  90. </view>
  91. </view>
  92. <view class="link" v-if="link">
  93. <uni-easyinput
  94. disabled
  95. v-model="link"
  96. />
  97. <button class="btn" @click="CopyLink(link)">
  98. {{ t('Ib.Index.Copy') }}
  99. </button>
  100. </view>
  101. </view>
  102. </view>
  103. </cwg-popup>
  104. <cwg-popup :visible="qrCodeDialogVisible" type="center" :title="t('Ib.Index.QrCode')" showFooterLine @singleClick="qrCodeDialogVisible = false" :singleBtnText="t('Btn.Cancel')" :footerType="'single'">
  105. <view class="qr-code-dialog-content">
  106. <QrCode
  107. ref="dialogQrCode"
  108. v-if="currentQrCodeLink"
  109. :text="currentQrCodeLink"
  110. :width="300"
  111. :height="300"
  112. ></QrCode>
  113. <view v-if="currentQrCodeLink" class="qr-code-btn">
  114. <button @click="downloadDialogQrCode">
  115. {{ t('Btn.item9') }}
  116. </button>
  117. </view>
  118. </view>
  119. </cwg-popup>
  120. </view>
  121. </cwg-page-wrapper>
  122. </template>
  123. <script setup lang="ts">
  124. import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
  125. import { onLoad } from '@dcloudio/uni-app'
  126. import { useI18n } from 'vue-i18n' // uni-app 中已集成,但需配置
  127. import { customApi } from '@/service/custom'
  128. import { financialApi } from '@/service/financial'
  129. import Config from '@/config/index'
  130. import PaymentMethodsList from './components/PaymentMethodsList.vue'
  131. import { ibApi } from '@/service/ib'
  132. import useUserStore from '@/stores/use-user-store'
  133. import QrCode from '@/components/QrCode.vue'
  134. import LinkDetailDialog from '@/pages/ib/components/linkDetailDialog.vue'
  135. const { Code, Host80 } = Config
  136. const { t, locale } = useI18n()
  137. // 列表数据
  138. const tableData = ref([])
  139. const loading = ref(false)
  140. const listApi = ref(null)
  141. const currentQrCodeLink = ref('')
  142. const qrCodeDialogVisible = ref(false)
  143. const datailVisible = ref(false)
  144. const detail = ref({})
  145. // 按账户类型分组的数据
  146. const accountTypeData = ref({
  147. ecn: [], // loginType = 2
  148. standard: [], // loginType = 7
  149. cent: [], // loginType = 8
  150. })
  151. const tableRef = ref(null)
  152. // 弹窗中的二维码组件
  153. const dialogQrCode = ref(null)
  154. const excludeList = ref([])
  155. const excludeLists = ref([])
  156. const excludeListVN = ref([])
  157. const excludeShowLoginTypes = ref([])
  158. // 账户类型设置:每个账户类型对应一个点差设置
  159. const accountTypeSettings = ref({
  160. // ECN账户,存储选中的完整对象
  161. ecn: { selectedIndex: null, selectedItem: null, loginType: '2' },
  162. // 标准账户
  163. standard: { selectedIndex: null, selectedItem: null, loginType: '7' },
  164. // 美分账户
  165. cent: { selectedIndex: null, selectedItem: null, loginType: '8' },
  166. })
  167. const selectedSpreadId = ref('')
  168. const linkName = ref('')
  169. const loginTypes = ref('')
  170. const link = ref('')
  171. const linkValue = ref('')
  172. const ibInvalid = ref('B0')
  173. const dialogLink = ref(false)
  174. const qrCode = ref(null)
  175. const { userInfo } = useUserStore()
  176. const country = computed(() => {
  177. return userInfo.customInfo.country
  178. })
  179. const agentAccountSetting = computed(() => {
  180. return userInfo.ibInfo.agentAccountSetting ||
  181. 0
  182. })
  183. const getInfoId = computed(() => {
  184. console.log(userInfo.ibInfo.id, 'userInfo.ibInfo.id')
  185. return userInfo.ibInfo.id ||
  186. 0
  187. })
  188. listApi.value = ibApi.customLinkSearchList
  189. const columns = ref([
  190. {
  191. prop: 'name',
  192. label: t('Ib.Custom.NameLabel'),
  193. align: 'center',
  194. },
  195. {
  196. prop: 'link',
  197. label: t('Ib.Index.Link'),
  198. align: 'center',
  199. slot: 'link',
  200. width: 450,
  201. },
  202. {
  203. prop: 'linkValue',
  204. label: t('Ib.Index.LinkValue'),
  205. align: 'center',
  206. slot: 'linkValue',
  207. },
  208. {
  209. prop: 'customNum',
  210. label: t('Ib.Index.CustomNum'),
  211. align: 'center',
  212. },
  213. {
  214. prop: 'comPoint',
  215. label: t('AccountType.StandardAccount'),
  216. align: 'center',
  217. formatter: ({ row }) => getGroupNameByLoginType(row.loginConfig, 7),
  218. },
  219. {
  220. prop: 'hide',
  221. label: 'ECN',
  222. align: 'center',
  223. formatter: ({ row }) => getGroupNameByLoginType(row.loginConfig, 2),
  224. },
  225. {
  226. prop: 'loginTypes',
  227. label: t('AccountType.CentAccount'),
  228. align: 'center',
  229. formatter: ({ row }) => getGroupNameByLoginType(row.loginConfig, 8),
  230. },
  231. ])
  232. const mobilePrimaryFields = ref([
  233. {
  234. prop: 'name',
  235. label: t('Ib.Custom.NameLabel'),
  236. align: 'center',
  237. },
  238. {
  239. prop: 'customNum',
  240. label: t('Ib.Index.CustomNum'),
  241. align: 'center',
  242. },
  243. {
  244. prop: 'more',
  245. type: 'more',
  246. width: 20,
  247. align: 'right',
  248. },
  249. ])
  250. const getGroupNameByLoginType = (loginConfig, loginType) => {
  251. if (!loginConfig) return ''
  252. // 如果 loginConfig 是字符串,尝试解析为数组
  253. let config = loginConfig
  254. if (typeof loginConfig === 'string') {
  255. try {
  256. config = JSON.parse(loginConfig)
  257. } catch (e) {
  258. return ''
  259. }
  260. }
  261. // 确保是数组
  262. if (!Array.isArray(config)) {
  263. return ''
  264. }
  265. // 查找匹配的 loginType
  266. const matchedItem = config.find(item => {
  267. // 支持数字和字符串类型的比较
  268. return item.loginType === loginType ||
  269. item.loginType === String(loginType) ||
  270. String(item.loginType) === String(loginType)
  271. })
  272. return matchedItem ? (matchedItem.groupName || '') : ''
  273. }
  274. const CopyLink = (link) => {
  275. uni.setClipboardData({
  276. data: link,
  277. success: () => {
  278. uni.showToast({
  279. title: t('card.Msg.m8'),
  280. icon: 'success',
  281. })
  282. },
  283. })
  284. }
  285. const showQrCodeDialog = (link) => {
  286. if (!link) return
  287. currentQrCodeLink.value = link
  288. qrCodeDialogVisible.value = true
  289. }
  290. // 获取开户链接账户类型列表
  291. const getCustomLinkTypes = async () => {
  292. let res = await ibApi.customLinkTypes()
  293. if (res.code == Code.StatusOK) {
  294. // 按 loginType 分组数据
  295. const data = res.data || []
  296. console.log(data, '123')
  297. accountTypeData.value = {
  298. ecn: data.filter(item => item.loginType === 2 || item.loginType === '2'),
  299. standard: data.filter(item => item.loginType === 7 || item.loginType === '7'),
  300. cent: data.filter(item => item.loginType === 8 || item.loginType === '8'),
  301. }
  302. } else {
  303. uni.showToast({
  304. title: res.msg,
  305. icon: 'none',
  306. })
  307. accountTypeData.value = {
  308. ecn: [],
  309. standard: [],
  310. cent: [],
  311. }
  312. }
  313. }
  314. // 获取账户类型设置
  315. const getAgentAccountSetting = () => {
  316. // 如果是越南账户,显示标准账户、美分账户和ECN账户
  317. if (country.value === 'VN') {
  318. const standardAccount = excludeLists.value.find(
  319. (item) => item.value === '7',
  320. )
  321. const centAccount = excludeLists.value.find(
  322. (item) => item.value === '8',
  323. )
  324. const ecnAccount = excludeLists.value.find(
  325. (item) => item.value === '2',
  326. )
  327. excludeListVN.value = [standardAccount, centAccount, ecnAccount].filter(
  328. Boolean,
  329. )
  330. excludeShowLoginTypes.value = []
  331. } else {
  332. if (agentAccountSetting.value == 0) {
  333. const excludeValues = userInfo
  334. .customInfo.excludeShowLoginTypes
  335. try {
  336. const excludeList = excludeLists.value.filter(
  337. (item) => !excludeValues.includes(item.value),
  338. )
  339. excludeList.value = excludeList
  340. excludeShowLoginTypes.value = []
  341. } catch (error) {
  342. excludeShowLoginTypes.value = []
  343. excludeList.value = excludeLists.value
  344. }
  345. } else {
  346. excludeShowLoginTypes.value = []
  347. excludeList.value = excludeLists.value
  348. }
  349. }
  350. }
  351. // add
  352. const addLink = async () => {
  353. console.log(getInfoId.value, 'getInfo.id')
  354. await getCustomLinkTypes()
  355. getAgentAccountSetting()
  356. // 重置所有表单字段
  357. selectedSpreadId.value = ''
  358. linkName.value = ''
  359. loginTypes.value = ''
  360. linkValue.value = ''
  361. // 重置账户类型设置
  362. accountTypeSettings.value = {
  363. ecn: { selectedIndex: null, selectedItem: null, loginType: '2' },
  364. standard: { selectedIndex: null, selectedItem: null, loginType: '7' },
  365. cent: { selectedIndex: null, selectedItem: null, loginType: '8' },
  366. }
  367. dialogLink.value = true
  368. }
  369. const onNameChange = (e)=>{
  370. console.log(e)
  371. }
  372. // 账户类型选择变化处理
  373. const handleAccountTypeChange = (type, loginType,val) => {
  374. console.log(type, loginType,val,'221')
  375. const setting = accountTypeSettings.value[type]
  376. const availableSpreads = getAvailableSpreads(loginType)
  377. if (setting.selectedIndex !== null && setting.selectedIndex !== undefined) {
  378. accountTypeSettings.value[type].selectedItem = availableSpreads[setting.selectedIndex] || null
  379. } else {
  380. accountTypeSettings.value[type].selectedItem = null
  381. }
  382. }
  383. // 获取指定账户类型可用的点差设置
  384. const getAvailableSpreads = (loginType) => {
  385. // 根据 loginType 返回对应的数据
  386. if (loginType === '2') {
  387. return accountTypeData.value.ecn?.map((item,index) => ({ ...item,value:index, text: item.groupName })) || []
  388. } else if (loginType === '7') {
  389. return accountTypeData.value.standard?.map((item,index) => ({ ...item,value:index, text: item.groupName })) || []
  390. } else if (loginType === '8') {
  391. return accountTypeData.value.cent?.map((item,index) => ({ ...item,value:index, text: item.groupName })) || []
  392. }
  393. return []
  394. }
  395. // 获取链接值
  396. const getLink1 = async () => {
  397. // 收集所有选中的账户类型和对应的点差设置
  398. const selectedAccountTypes = []
  399. const loginConfig = []
  400. // 检查每个账户类型是否已选择点差设置
  401. Object.keys(accountTypeSettings.value).forEach((key) => {
  402. const setting = accountTypeSettings.value[key]
  403. if (setting.selectedItem) {
  404. selectedAccountTypes.push(setting.loginType)
  405. // 将选中的整个对象添加到 loginConfig 数组
  406. loginConfig.push(setting.selectedItem)
  407. }
  408. })
  409. if (selectedAccountTypes.length === 0) {
  410. uni.showToast({
  411. title: t('Ib.Index.Spread5'),
  412. icon: 'none',
  413. })
  414. link.value = ''
  415. return
  416. }
  417. // 构建请求参数
  418. const validList = {
  419. loginConfig: loginConfig,
  420. }
  421. // 直接调用接口生成链接
  422. const res = await ibApi.customLinkCode(validList)
  423. if (res.code === Code.StatusOK) {
  424. uni.showToast({
  425. title: res.msg,
  426. icon: 'none',
  427. })
  428. return res.data
  429. } else {
  430. uni.showToast({
  431. title: res.msg,
  432. icon: 'error',
  433. })
  434. return ''
  435. }
  436. }
  437. const saveLink = async () => {
  438. if (!linkName.value) {
  439. uni.showToast({
  440. title: t("Ib.Custom.NameLabel"),
  441. icon: 'none',
  442. })
  443. return;
  444. }
  445. if (!link.value) {
  446. uni.showToast({
  447. title: t("Ib.Index.CreateLink"),
  448. icon: 'none',
  449. })
  450. return;
  451. }
  452. // 收集所有选中的账户类型配置
  453. const loginConfig = [];
  454. Object.keys(accountTypeSettings.value).forEach((key) => {
  455. const setting = accountTypeSettings.value[key];
  456. if (setting.selectedItem) {
  457. // 将选中的整个对象添加到 loginConfig 数组
  458. loginConfig.push(setting.selectedItem);
  459. }
  460. });
  461. if (loginConfig.length === 0) {
  462. uni.showToast({
  463. title: t("Ib.Index.Spread5"),
  464. icon: 'none',
  465. })
  466. return;
  467. }
  468. // 调用保存接口
  469. const res = await ibApi.customLinkAdd({
  470. name: linkName.value,
  471. link: link.value,
  472. linkValue: linkValue.value,
  473. loginConfig: loginConfig,
  474. });
  475. if (res.code === Code.StatusOK) {
  476. uni.showToast({
  477. title: res.msg,
  478. icon: 'none',
  479. })
  480. dialogLink.value = false;
  481. // 重置表单
  482. linkName.value = "";
  483. link.value = "";
  484. linkValue.value = "";
  485. excludeShowLoginTypes.value = [];
  486. selectedSpreadId.value = "";
  487. accountTypeSettings.value = {
  488. ecn: { selectedIndex: null, selectedItem: null, loginType: "2" },
  489. standard: { selectedIndex: null, selectedItem: null, loginType: "7" },
  490. cent: { selectedIndex: null, selectedItem: null, loginType: "8" },
  491. };
  492. tableRef.value.refreshTable()
  493. } else {
  494. uni.showToast({
  495. title: res.msg,
  496. icon: 'error',
  497. })
  498. }
  499. }
  500. // 生成链接(不保存)
  501. const CreateLink = async () => {
  502. console.log(linkName.value)
  503. if (!linkName.value) {
  504. uni.showToast({
  505. title: t('Ib.Custom.NameLabel'),
  506. icon: 'none',
  507. })
  508. return
  509. }
  510. const value = await getLink1()
  511. if (!value) return
  512. linkValue.value = value
  513. link.value = `${Host80}/#/signup/${getInfoId.value}/${value}/${ibInvalid.value}`
  514. }
  515. const downloadQrCode = (type = 0) => {
  516. qrCode.value.download()
  517. }
  518. // 下载弹窗中的二维码
  519. const downloadDialogQrCode = (type = 0) => {
  520. dialogQrCode.value.download()
  521. }
  522. // 打开详情弹窗
  523. const openDetail = (row) => {
  524. datailVisible.value = true
  525. detail.value = row
  526. }
  527. // 关闭详情弹窗
  528. const closeDetail = () => {
  529. console.log('asd')
  530. datailVisible.value = false
  531. detail.value = {}
  532. }
  533. </script>
  534. <style lang="scss" scoped>
  535. @import "@/uni.scss";
  536. .add-link {
  537. width: 100%;
  538. display: flex;
  539. align-items: center;
  540. justify-content: flex-end;
  541. .add-link-content {
  542. cursor: pointer;
  543. display: flex;
  544. align-items: center;
  545. color: #fff;
  546. padding: 0 px2rpx(10);
  547. background-color: #ffde02;
  548. font-size: px2rpx(16);
  549. font-weight: bold;
  550. line-height: px2rpx(26);
  551. border-radius: px2rpx(5);
  552. }
  553. }
  554. .link-cell {
  555. width: 100%;
  556. display: inline-flex;
  557. align-items: center;
  558. justify-content: center;
  559. gap: 10px;
  560. white-space: nowrap;
  561. .read-input {
  562. max-width: px2rpx(400);
  563. white-space: nowrap;
  564. overflow: hidden;
  565. text-overflow: ellipsis;
  566. flex: 1;
  567. }
  568. .copy {
  569. cursor: pointer;
  570. line-height: px2rpx(28);
  571. }
  572. }
  573. .qr-code-icon {
  574. cursor: pointer;
  575. user-select: none;
  576. transition: opacity 0.3s;
  577. white-space: nowrap;
  578. line-height: 28px;
  579. display: flex;
  580. align-items: center;
  581. padding: 0 8px;
  582. &:hover {
  583. opacity: 0.7;
  584. }
  585. }
  586. .dia-content {
  587. padding: 20rpx;
  588. }
  589. .content {
  590. display: flex;
  591. flex-direction: column;
  592. gap: 20rpx;
  593. }
  594. .label-tit{
  595. font-weight: 500;
  596. margin-bottom: 8rpx;
  597. }
  598. .label {
  599. font-weight: 400;
  600. margin-bottom: 8rpx;
  601. }
  602. .btn {
  603. margin-top: px2rpx(16);
  604. text-align: center;
  605. }
  606. .link {
  607. display: flex;
  608. margin-top: px2rpx(20);
  609. .btn {
  610. display: flex;
  611. align-items: center;
  612. justify-content: center;
  613. height: px2rpx(35);
  614. margin: 0 px2rpx(10);
  615. }
  616. }
  617. .qrCode {
  618. display: flex;
  619. flex-direction: column;
  620. align-items: center;
  621. gap: px2rpx(16);
  622. }
  623. .qr-code-dialog-content {
  624. display: flex;
  625. flex-direction: column;
  626. justify-content: center;
  627. align-items: center;
  628. padding: px2rpx(20) 0;
  629. .qr-code-btn{
  630. margin-top: px2rpx(16);
  631. }
  632. }
  633. </style>