Quellcode durchsuchen

feat(gameEvent): 添加自定义字体上传和云端字体库功能

- 在BIB预览对话框中添加字体上传组件,支持TF/OTF格式字体文件上传
- 集成云端字体库功能,可从OSS资源中选择已上传的字体文件
- 实现自定义字体的动态加载和预览功能,包括字体加载状态显示
- 添加字体预览加载动画,提升用户体验
- 支持自定义字体在BIB任务创建时正确传递字体ID参数
zhou vor 2 Wochen
Ursprung
Commit
0659dd99c7
1 geänderte Dateien mit 140 neuen und 2 gelöschten Zeilen
  1. 140 2
      src/views/system/gameEvent/components/bibViewerDialog.vue

+ 140 - 2
src/views/system/gameEvent/components/bibViewerDialog.vue

@@ -69,6 +69,7 @@
           <div
             v-if="bibForm.eventName"
             class="event-name-preview draggable-element"
+            v-loading="fontPreviewLoading"
             :style="{
               fontSize: Math.min(bibForm.fontSize, 56) + 'px',
               color: bibForm.fontColorHex,
@@ -88,6 +89,9 @@
           <!-- 示例数字 12345 -->
           <div
             class="draggable-element number-element"
+            v-loading="fontPreviewLoading"
+            element-loading-text="加载字体..."
+            element-loading-background="rgba(255, 255, 255, 0.7)"
             :style="{
               left: (bibForm.numberX || 50) + '%',
               top: (bibForm.numberY || 50) + '%',
@@ -163,14 +167,27 @@
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="字体设置">
-              <div style="display: flex; gap: 15px; align-items: center">
-                <el-select v-model="bibForm.fontName" placeholder="字体" style="width: 100px">
+              <div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap">
+                <el-select v-model="bibForm.fontName" filterable clearable placeholder="字体" style="width: 100px">
                   <el-option label="黑体" value="simhei"></el-option>
                   <el-option label="宋体" value="simsun"></el-option>
                   <el-option label="微软雅黑" value="yahei"></el-option>
+                  <el-option v-if="bibForm.isCustomFont" :label="bibForm.customFontName" :value="bibForm.customFontNameValue"></el-option>
                 </el-select>
                 <el-input-number v-model="bibForm.fontSize" :min="8" :max="198" placeholder="字体大小" style="width: 140px"></el-input-number>
                 <el-color-picker v-model="bibForm.fontColor" @change="handleFontColorChange"></el-color-picker>
+                <el-upload
+                  class="font-upload"
+                  :auto-upload="true"
+                  :show-file-list="false"
+                  :action="uploadUrl"
+                  :headers="uploadHeaders"
+                  :on-success="handleFontUploadSuccess"
+                  accept=".ttf,.otf"
+                >
+                  <el-button type="primary" link icon="Upload">上传字体</el-button>
+                </el-upload>
+                <el-button type="primary" link icon="FolderOpened" @click="openCloudFontDialog">云端字体库</el-button>
               </div>
             </el-form-item>
           </el-col>
@@ -231,11 +248,31 @@
       </div>
     </template>
   </el-dialog>
+
+  <!-- 云端字体选择对话框 -->
+  <el-dialog v-model="cloudFontVisible" title="选择云端字体" width="600px" append-to-body>
+    <el-table v-loading="fontLoading" :data="fontList" @row-click="handleCloudFontSelect" highlight-current-row>
+      <el-table-column label="字体名称" align="center" prop="originalName" />
+      <el-table-column label="上传时间" align="center" prop="createTime" width="180" />
+      <el-table-column label="操作" align="center" width="100">
+        <template #default="scope">
+          <el-button link type="primary" @click="handleCloudFontSelect(scope.row)">选择</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="cloudFontVisible = false">取 消</el-button>
+      </div>
+    </template>
+  </el-dialog>
 </template>
 
 <script setup lang="ts">
 import { ref, reactive, nextTick, getCurrentInstance } from 'vue';
 import { createBibTask } from '@/api/system/gameEvent/task';
+import { listOss } from '@/api/system/oss';
+import { getToken } from '@/utils/auth';
 import type { UploadInstance } from 'element-plus';
 import { parseTime } from '@/utils/ruoyi';
 
@@ -294,8 +331,24 @@ const bibForm = reactive({
   barcodeScale: 1,
   numberScale: 1,
   eventScale: 1,
+  // 字体相关
+  fontOssId: null as number | null,
+  isCustomFont: false,
+  customFontName: '',
+  customFontNameValue: '' // 存储自定义字体的内部标识符,如 CustomFont_123
 });
 
+const uploadHeaders = ref({
+  Authorization: 'Bearer ' + getToken(),
+  clientid: import.meta.env.VITE_APP_CLIENT_ID
+});
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload');
+
+const cloudFontVisible = ref(false);
+const fontLoading = ref(false);
+const fontPreviewLoading = ref(false); // 字体预览加载状态
+const fontList = ref<any[]>([]);
+
 const bgImageFile = ref<File | null>(null);
 const logoImageFile = ref<File | null>(null);
 const bgImageUrl = ref<string>('');
@@ -405,6 +458,12 @@ const resetBibForm = () => {
   selectedElement.value = '';
   // 重置画布比例
   canvasScale.value = 1;
+  
+  // 重置自定义字体
+  bibForm.fontOssId = null;
+  bibForm.isCustomFont = false;
+  bibForm.customFontName = '';
+  bibForm.customFontNameValue = '';
 
   // 清除上传的文件
   if (bgUploadRef.value) {
@@ -491,6 +550,84 @@ const handleFontColorChange = (color: string) => {
   bibForm.fontColorHex = color;
 };
 
+// 上传字体成功回调
+const handleFontUploadSuccess = (response: any) => {
+  if (response.code === 200) {
+    const ossData = response.data;
+    loadCustomFont(ossData.url, ossData.originalName, ossData.ossId);
+    proxy?.$modal.msgSuccess('字体上传成功');
+  } else {
+    proxy?.$modal.msgError(response.msg || '上传失败');
+  }
+};
+
+// 打开云端字体对话框
+const openCloudFontDialog = async () => {
+  cloudFontVisible.value = true;
+  fontLoading.value = true;
+  try {
+    const res = await listOss({ fileSuffix: '.ttf' } as any);
+    const resOtf = await listOss({ fileSuffix: '.otf' } as any);
+    fontList.value = [...(res.rows || []), ...(resOtf.rows || [])];
+  } finally {
+    fontLoading.value = false;
+  }
+};
+
+// 选择云端字体
+const handleCloudFontSelect = (row: any) => {
+  loadCustomFont(row.url, row.originalName, row.ossId);
+  cloudFontVisible.value = false;
+};
+
+// 加载并注册自定义字体以供预览
+const loadCustomFont = async (url: string, name: string, ossId: number) => {
+  // 1. 处理名称:去除后缀
+  const cleanName = name.replace(/\.[^/.]+$/, "");
+  const fontFamily = `CustomFont_${ossId}`;
+  
+  // 2. 检查缓存:如果该字体已经加载过,直接应用
+  const existingFont = Array.from(document.fonts).find(f => f.family === fontFamily);
+  if (existingFont && existingFont.status === 'loaded') {
+    bibForm.fontName = fontFamily;
+    bibForm.fontOssId = ossId;
+    bibForm.isCustomFont = true;
+    bibForm.customFontName = cleanName;
+    bibForm.customFontNameValue = fontFamily;
+    return;
+  }
+
+  fontPreviewLoading.value = true;
+  try {
+    const fontFace = new FontFace(fontFamily, `url(${url})`);
+    // 显式添加到集合中
+    document.fonts.add(fontFace);
+    
+    // 3. 等待加载完成
+    await fontFace.load();
+    
+    // 4. 更新状态
+    bibForm.fontName = fontFamily;
+    bibForm.fontOssId = ossId;
+    bibForm.isCustomFont = true;
+    bibForm.customFontName = cleanName;
+    bibForm.customFontNameValue = fontFamily;
+    
+    proxy?.$modal.msgSuccess(`成功加载字体: ${cleanName}`);
+  } catch (error) {
+    console.error('加载字体文件失败:', error);
+    proxy?.$modal.msgError('加载预览字体失败');
+    
+    // 即使加载失败也记录信息
+    bibForm.fontName = fontFamily;
+    bibForm.fontOssId = ossId;
+    bibForm.isCustomFont = true;
+    bibForm.customFontName = cleanName;
+  } finally {
+    fontPreviewLoading.value = false;
+  }
+};
+
 // 开始拖拽
 const startDrag = (event: MouseEvent, target: string) => {
   event.preventDefault();
@@ -709,6 +846,7 @@ const handleCreateTask = async () => {
       
       // 其他通用参数
       fontName: bibForm.fontName || 'simhei',
+      fontOssId: bibForm.fontOssId,
       fontSize: bibForm.fontSize || 36,
       fontColor: parseInt((bibForm.fontColor || '#000000').replace('#', ''), 16),
       eventName: bibForm.eventName || '',