QrCodeModal.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. <script setup>
  2. import { computed, ref, watch } from 'vue';
  3. import { useI18n } from 'vue-i18n';
  4. import { Protocols } from '@/models/inbound.js';
  5. import QrPanel from './QrPanel.vue';
  6. const { t } = useI18n();
  7. const props = defineProps({
  8. open: { type: Boolean, default: false },
  9. dbInbound: { type: Object, default: null },
  10. client: { type: Object, default: null },
  11. remarkModel: { type: String, default: '-ieo' },
  12. nodeAddress: { type: String, default: '' },
  13. subSettings: {
  14. type: Object,
  15. default: () => ({ enable: false, subURI: '', subJsonURI: '', subJsonEnable: false }),
  16. },
  17. });
  18. const emit = defineEmits(['update:open']);
  19. const links = ref([]);
  20. const wireguardConfigs = ref([]);
  21. const wireguardLinks = ref([]);
  22. const subLink = ref('');
  23. const subJsonLink = ref('');
  24. const activeKeys = ref([]);
  25. const qrItems = computed(() => {
  26. const items = [];
  27. if (subLink.value) {
  28. items.push({
  29. key: 'sub',
  30. header: t('subscription.title'),
  31. value: subLink.value,
  32. });
  33. }
  34. if (subJsonLink.value) {
  35. items.push({
  36. key: 'sub-json',
  37. header: `${t('subscription.title')} (JSON)`,
  38. value: subJsonLink.value,
  39. });
  40. }
  41. links.value.forEach((link, idx) => {
  42. items.push({
  43. key: `l${idx}`,
  44. header: link.remark || `Link ${idx + 1}`,
  45. value: link.link,
  46. });
  47. });
  48. wireguardConfigs.value.forEach((cfg, idx) => {
  49. items.push({
  50. key: `wc${idx}`,
  51. header: `Peer ${idx + 1} config`,
  52. value: cfg,
  53. downloadName: `peer-${idx + 1}.conf`,
  54. });
  55. if (wireguardLinks.value[idx]) {
  56. items.push({
  57. key: `wl${idx}`,
  58. header: `Peer ${idx + 1} link`,
  59. value: wireguardLinks.value[idx],
  60. });
  61. }
  62. });
  63. return items;
  64. });
  65. watch(() => props.open, (next) => {
  66. if (!next || !props.dbInbound) return;
  67. const inbound = props.dbInbound.toInbound();
  68. if (inbound.protocol === Protocols.WIREGUARD) {
  69. const peerRemark = props.client?.email
  70. ? `${props.dbInbound.remark}-${props.client.email}`
  71. : props.dbInbound.remark;
  72. wireguardConfigs.value = inbound.genWireguardConfigs(peerRemark, '-ieo', props.nodeAddress).split('\r\n');
  73. wireguardLinks.value = inbound.genWireguardLinks(peerRemark, '-ieo', props.nodeAddress).split('\r\n');
  74. links.value = [];
  75. } else {
  76. // When a client is provided we generate per-client share links;
  77. // otherwise (single-user SS) fall back to the inbound's settings.
  78. links.value = inbound.genAllLinks(props.dbInbound.remark, props.remarkModel, props.client, props.nodeAddress);
  79. wireguardConfigs.value = [];
  80. wireguardLinks.value = [];
  81. }
  82. const subId = props.client?.subId;
  83. if (props.subSettings?.enable && subId) {
  84. subLink.value = (props.subSettings.subURI || '') + subId;
  85. subJsonLink.value = props.subSettings.subJsonEnable
  86. ? (props.subSettings.subJsonURI || '') + subId
  87. : '';
  88. } else {
  89. subLink.value = '';
  90. subJsonLink.value = '';
  91. }
  92. const open = [];
  93. if (subLink.value) open.push('sub');
  94. activeKeys.value = open;
  95. });
  96. function close() {
  97. emit('update:open', false);
  98. }
  99. </script>
  100. <template>
  101. <a-modal :open="open" :title="t('qrCode')" :footer="null" width="420px" @cancel="close">
  102. <template v-if="dbInbound">
  103. <a-collapse v-model:active-key="activeKeys" ghost class="qr-collapse">
  104. <a-collapse-panel v-for="item in qrItems" :key="item.key" :header="item.header">
  105. <QrPanel :value="item.value" :remark="item.header" :download-name="item.downloadName || ''"
  106. :show-qr="!item.value.includes('mldsa65') && !item.value.includes('ML-KEM-768')" />
  107. </a-collapse-panel>
  108. </a-collapse>
  109. </template>
  110. </a-modal>
  111. </template>
  112. <style scoped>
  113. .qr-collapse :deep(.ant-collapse-content-box) {
  114. padding: 8px 0 0;
  115. }
  116. </style>