index.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <template>
  2. <div class="p-4">
  3. <el-card v-loading="loading" shadow="never">
  4. <template #header>
  5. <div class="card-header">
  6. <span class="text-lg font-semibold">{{ t('applet.title') }}</span>
  7. </div>
  8. </template>
  9. <el-tabs v-if="appletData" v-model="activeTab" type="border-card">
  10. <el-tab-pane :label="t('applet.tab.userAgreement')" name="userAgreement">
  11. <el-tabs v-model="userAgreementLang" type="card" class="mb-4">
  12. <el-tab-pane :label="t('applet.lang.chinese')" name="zh_CN">
  13. <editor v-model="userAgreementData.zh_CN" :min-height="400"/>
  14. </el-tab-pane>
  15. <el-tab-pane :label="t('applet.lang.english')" name="en_US">
  16. <editor v-model="userAgreementData.en_US" :min-height="400"/>
  17. </el-tab-pane>
  18. </el-tabs>
  19. <div class="mt-4">
  20. <el-button type="primary" :loading="saveLoading" @click="handleSave">{{ t('applet.button.save') }}</el-button>
  21. </div>
  22. </el-tab-pane>
  23. <el-tab-pane :label="t('applet.tab.privacyAgreement')" name="privacyAgreement">
  24. <el-tabs v-model="privacyAgreementLang" type="card" class="mb-4">
  25. <el-tab-pane :label="t('applet.lang.chinese')" name="zh_CN">
  26. <editor v-model="privacyAgreementData.zh_CN" :min-height="400"/>
  27. </el-tab-pane>
  28. <el-tab-pane :label="t('applet.lang.english')" name="en_US">
  29. <editor v-model="privacyAgreementData.en_US" :min-height="400"/>
  30. </el-tab-pane>
  31. </el-tabs>
  32. <div class="mt-4">
  33. <el-button type="primary" :loading="saveLoading" @click="handleSave">{{ t('applet.button.save') }}</el-button>
  34. </div>
  35. </el-tab-pane>
  36. </el-tabs>
  37. <el-empty v-else :description="t('applet.message.noData')" />
  38. </el-card>
  39. </div>
  40. </template>
  41. <script setup name="Applet" lang="ts">
  42. import { getApplet, updateApplet } from '@/api/setting/applet';
  43. import { AppletVO } from '@/api/setting/applet/types';
  44. import { useI18n } from 'vue-i18n';
  45. const { t } = useI18n();
  46. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  47. const loading = ref(true);
  48. const saveLoading = ref(false);
  49. const activeTab = ref('userAgreement');
  50. const userAgreementLang = ref('zh_CN');
  51. const privacyAgreementLang = ref('zh_CN');
  52. const appletData = ref<AppletVO | null>(null);
  53. // 用户协议数据(中英文)
  54. const userAgreementData = ref({
  55. zh_CN: '',
  56. en_US: ''
  57. });
  58. // 隐私协议数据(中英文)
  59. const privacyAgreementData = ref({
  60. zh_CN: '',
  61. en_US: ''
  62. });
  63. /**
  64. * 解析协议数据
  65. * 支持三种格式:
  66. * 1. BASE64编码的JSON字符串
  67. * 2. 直接的JSON字符串
  68. * 3. 普通字符串(兼容旧数据)
  69. */
  70. const parseAgreementData = (data: string) => {
  71. if (!data) {
  72. return { zh_CN: '', en_US: '' };
  73. }
  74. try {
  75. // 尝试BASE64解码
  76. const decoded = decodeURIComponent(escape(atob(data)));
  77. const parsed = JSON.parse(decoded);
  78. return {
  79. zh_CN: parsed.zh_CN || '',
  80. en_US: parsed.en_US || ''
  81. };
  82. } catch (e) {
  83. // BASE64解码失败,尝试直接JSON解析
  84. try {
  85. const parsed = JSON.parse(data);
  86. return {
  87. zh_CN: parsed.zh_CN || '',
  88. en_US: parsed.en_US || ''
  89. };
  90. } catch (e2) {
  91. // JSON解析也失败,作为普通字符串处理(旧格式数据)
  92. return {
  93. zh_CN: data,
  94. en_US: ''
  95. };
  96. }
  97. }
  98. };
  99. /** 获取小程序设置数据 */
  100. const fetchData = async () => {
  101. loading.value = true;
  102. try {
  103. const res = await getApplet(1);
  104. appletData.value = res.data;
  105. // 解析用户协议
  106. if (appletData.value?.userAgreement) {
  107. userAgreementData.value = parseAgreementData(appletData.value.userAgreement);
  108. }
  109. // 解析隐私协议
  110. if (appletData.value?.privacyAgreement) {
  111. privacyAgreementData.value = parseAgreementData(appletData.value.privacyAgreement);
  112. }
  113. } catch (error) {
  114. console.error(t('applet.message.fetchFailed'), error);
  115. } finally {
  116. loading.value = false;
  117. }
  118. }
  119. /** 保存数据 */
  120. const handleSave = async () => {
  121. if (!appletData.value) return;
  122. saveLoading.value = true;
  123. try {
  124. // 将中英文协议整理成JSON格式,然后进行BASE64编码
  125. const userAgreementJson = JSON.stringify({
  126. zh_CN: userAgreementData.value.zh_CN || '',
  127. en_US: userAgreementData.value.en_US || ''
  128. });
  129. const privacyAgreementJson = JSON.stringify({
  130. zh_CN: privacyAgreementData.value.zh_CN || '',
  131. en_US: privacyAgreementData.value.en_US || ''
  132. });
  133. const encodedData = {
  134. ...appletData.value,
  135. id: 1, // 默认设置为1
  136. userAgreement: btoa(unescape(encodeURIComponent(userAgreementJson))),
  137. privacyAgreement: btoa(unescape(encodeURIComponent(privacyAgreementJson)))
  138. };
  139. await updateApplet(encodedData);
  140. proxy?.$modal.msgSuccess(t('applet.message.saveSuccess'));
  141. } catch (error) {
  142. console.error(t('applet.message.saveFailed'), error);
  143. } finally {
  144. saveLoading.value = false;
  145. }
  146. }
  147. onMounted(() => {
  148. fetchData();
  149. });
  150. </script>