1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<script setup lang="ts">
import { message } from 'ant-design-vue';
import { Button, Input, Upload } from 'ant-design-vue';
import { onMounted, ref } from 'vue';
 
// 注意:本文件位于 @vben 的 packages 内部,无法使用应用层别名 #/,需用相对路径(与同目录 custom.vue 保持一致)
import { publicUploadFile } from '../../../../../../../../api/system/storage';
import { $t } from '../../../../../../../locales/src';
import { useCompanyConfigStore } from '../../../../../../../../store/company-config';
 
defineOptions({
  name: 'PreferenceCompanyConfig',
});
 
const companyConfigStore = useCompanyConfigStore();
 
const companyName = ref(companyConfigStore.companyName);
const loginTitle = ref(companyConfigStore.loginTitle);
const loginDescription = ref(companyConfigStore.loginDescription);
const logoUrl = ref(companyConfigStore.logoUrl);
const uploading = ref(false);
 
// 打开区块时从后端拉取最新公司配置并回填表单(store 初值可能为空,因启动加载走的是纯函数)
onMounted(async () => {
  await companyConfigStore.loadFromServer();
  companyName.value = companyConfigStore.companyName;
  loginTitle.value = companyConfigStore.loginTitle;
  loginDescription.value = companyConfigStore.loginDescription;
  logoUrl.value = companyConfigStore.logoUrl;
});
 
/** 上传 logo(永久公开,供登录页免登录展示) */
async function handleBeforeUpload(file: File) {
  uploading.value = true;
  try {
    const blobs = await publicUploadFile([file]);
    const blob = blobs?.[0];
    if (blob?.previewURL || blob?.url) {
      logoUrl.value = blob.previewURL || blob.url || '';
      message.success($t('preferences.company.uploadSuccess'));
    } else {
      message.error($t('preferences.company.uploadFailed'));
    }
  } catch {
    message.error($t('preferences.company.uploadFailed'));
  } finally {
    uploading.value = false;
  }
  // 返回 false,阻止组件默认上传(已通过接口上传)
  return false;
}
 
async function handleSave() {
  if (!companyName.value.trim()) {
    message.warning($t('preferences.company.companyNameRequired'));
    return;
  }
  try {
    await companyConfigStore.save({
      companyName: companyName.value.trim(),
      logoUrl: logoUrl.value || undefined,
      loginTitle: loginTitle.value,
      loginDescription: loginDescription.value,
    });
    message.success($t('preferences.company.saveSuccess'));
  } catch {
    message.error($t('preferences.company.saveFailed'));
  }
}
</script>
 
<template>
  <div class="flex flex-col">
    <!-- 公司名称 -->
    <div class="my-1 flex w-full items-center justify-between rounded-md px-2 py-1">
      <span class="flex items-center text-sm">
        {{ $t('preferences.company.companyName') }}
      </span>
      <Input
        v-model:value="companyName"
        :placeholder="$t('preferences.company.companyNamePlaceholder')"
        class="w-41.25"
        allow-clear
      />
    </div>
 
    <!-- 登录页标题 -->
    <div class="my-1 flex w-full items-center justify-between rounded-md px-2 py-1">
      <span class="flex items-center text-sm">
        {{ $t('preferences.company.loginTitle') }}
      </span>
      <Input
        v-model:value="loginTitle"
        :placeholder="$t('preferences.company.loginTitlePlaceholder')"
        class="w-41.25"
        allow-clear
      />
    </div>
 
    <!-- 登录页描述 -->
    <div class="my-1 flex w-full items-center justify-between rounded-md px-2 py-1">
      <span class="flex items-center text-sm">
        {{ $t('preferences.company.loginDescription') }}
      </span>
      <Input
        v-model:value="loginDescription"
        :placeholder="$t('preferences.company.loginDescriptionPlaceholder')"
        class="w-41.25"
        allow-clear
      />
    </div>
 
    <!-- 公司 Logo -->
    <div class="my-1 flex w-full items-center justify-between rounded-md px-2 py-1">
      <span class="flex items-center text-sm">
        {{ $t('preferences.company.logo') }}
      </span>
      <div class="flex items-center gap-2">
        <Upload
          :show-upload-list="false"
          accept="image/*"
          :before-upload="handleBeforeUpload"
        >
          <Button size="small" :loading="uploading">
            {{ $t('preferences.company.uploadLogo') }}
          </Button>
        </Upload>
        <img
          v-if="logoUrl"
          :src="logoUrl"
          alt="company-logo"
          class="h-9 w-9 rounded border border-border object-contain"
        />
        <span v-else class="text-xs text-muted-foreground">
          {{ $t('preferences.company.logoEmpty') }}
        </span>
      </div>
    </div>
 
    <div v-if="uploading" class="px-2 pb-1 text-xs text-muted-foreground">
      {{ $t('preferences.company.uploading') }}
    </div>
 
    <!-- 保存 -->
    <div class="mt-3 px-2">
      <Button type="primary" class="w-full" @click="handleSave">
        {{ $t('preferences.company.save') }}
      </Button>
    </div>
  </div>
</template>