Преглед изворни кода

修复高校管理
复习资料增加show_type,file_url管理

罗永浩 пре 5 месеци
родитељ
комит
7dc594726c

+ 101 - 1
addons/admin/src/views/exam/articleList.vue

@@ -58,12 +58,35 @@
                             </el-tag>
                         </template>
                     </el-table-column>
+                    <el-table-column label="内容类型" width="120" align="center">
+                        <template slot-scope="{ row }">
+                            <el-tag :type="getShowTypeTagType(row.show_type)" size="small">
+                                {{ getShowTypeLabel(row.show_type) }}
+                            </el-tag>
+                        </template>
+                    </el-table-column>
                     <el-table-column label="内容" min-width="200">
                         <template slot-scope="{ row }">
-                            <div class="content-preview" @click="openForm(row)">
+                            <!-- 默认编辑器类型 -->
+                            <div v-if="row.show_type === 1 || !row.show_type" class="content-preview"
+                                @click="openForm(row)">
                                 <div v-html="getContentPreview(row.content)" class="content-text"></div>
                                 <el-button type="text" size="mini" class="view-more-btn">查看全部</el-button>
                             </div>
+                            <!-- Word文档类型 -->
+                            <div v-else-if="row.show_type === 2" class="file-preview" @click="openForm(row)">
+                                <i class="el-icon-document" style="font-size: 24px; color: #409eff;"></i>
+                                <span class="file-name">{{ getFileName(row.file_url) || '点击上传Word文档' }}</span>
+                                <el-button type="text" size="mini" class="view-more-btn">查看</el-button>
+                            </div>
+                            <!-- 图片类型 -->
+                            <div v-else-if="row.show_type === 3" class="image-preview" @click="openForm(row)">
+                                <el-image v-if="row.file_url" :src="row.file_url" :preview-src-list="[row.file_url]"
+                                    fit="cover" style="width: 60px; height: 60px; border-radius: 4px;">
+                                </el-image>
+                                <span v-else class="no-image">未上传图片</span>
+                                <el-button type="text" size="mini" class="view-more-btn">查看</el-button>
+                            </div>
                         </template>
                     </el-table-column>
                     <el-table-column label="操作" width="160" fixed="right">
@@ -285,6 +308,30 @@ export default {
             const preview = textContent.length > 100 ? textContent.substring(0, 100) + '...' : textContent;
             return preview;
         },
+        // 获取显示类型标签
+        getShowTypeLabel(showType) {
+            const map = {
+                1: '默认编辑器',
+                2: 'Word文档',
+                3: '图片'
+            };
+            return map[showType] || '默认编辑器';
+        },
+        // 获取显示类型标签颜色
+        getShowTypeTagType(showType) {
+            const map = {
+                1: 'primary',
+                2: 'success',
+                3: 'warning'
+            };
+            return map[showType] || 'primary';
+        },
+        // 从URL提取文件名
+        getFileName(url) {
+            if (!url) return '';
+            const parts = url.split('/');
+            return parts[parts.length - 1] || '';
+        },
         // 处理内容中的图片URL
         processImageUrls(content) {
             if (!content) return content;
@@ -426,4 +473,57 @@ export default {
 .view-more-btn:hover {
     color: #66b1ff;
 }
+
+/* 文件预览样式 */
+.file-preview {
+    padding: 8px;
+    cursor: pointer;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    background-color: #f8f9fa;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    transition: all 0.3s ease;
+    min-height: 60px;
+}
+
+.file-preview:hover {
+    background-color: #e9ecef;
+    border-color: #409eff;
+}
+
+.file-name {
+    flex: 1;
+    font-size: 12px;
+    color: #606266;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+/* 图片预览样式 */
+.image-preview {
+    padding: 8px;
+    cursor: pointer;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    background-color: #f8f9fa;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    transition: all 0.3s ease;
+    min-height: 76px;
+}
+
+.image-preview:hover {
+    background-color: #e9ecef;
+    border-color: #409eff;
+}
+
+.no-image {
+    font-size: 12px;
+    color: #909399;
+    font-style: italic;
+}
 </style>

+ 68 - 131
addons/admin/src/views/exam/component/ArticleForm.vue

@@ -19,9 +19,9 @@
                     </el-select>
                 </el-form-item>
 
-                <el-form-item label="内容类型" prop="content_type">
-                    <el-radio-group v-model="formData.content_type" @change="onContentTypeChange">
-                        <el-radio :label="1">富文本编辑</el-radio>
+                <el-form-item label="内容类型" prop="show_type">
+                    <el-radio-group v-model="formData.show_type" @change="onContentTypeChange">
+                        <el-radio :label="1">默认编辑器</el-radio>
                         <el-radio :label="2">Word文档</el-radio>
                         <el-radio :label="3">图片</el-radio>
                     </el-radio-group>
@@ -29,25 +29,24 @@
 
                 <el-form-item label="内容" prop="content">
                     <!-- 富文本编辑器 -->
-                    <tinymce-editor v-if="formData.content_type === 1" v-model="formData.content"
-                        :init="editorConfig" />
+                    <tinymce-editor v-if="formData.show_type === 1" v-model="formData.content" :init="editorConfig" />
 
                     <!-- Word文档上传 -->
-                    <div v-else-if="formData.content_type === 2" class="word-upload-container">
+                    <div v-else-if="formData.show_type === 2" class="word-upload-container">
                         <el-upload class="word-uploader" :show-file-list="false" :before-upload="beforeWordUpload"
                             :auto-upload="false" :on-change="onWordFileChange" accept=".doc,.docx">
                             <el-button type="primary" icon="el-icon-upload">选择Word文档</el-button>
                         </el-upload>
-                        <div v-if="formData.word_file" class="word-file-info">
+                        <div v-if="formData.file_url || formData.word_file_name" class="word-file-info">
                             <i class="el-icon-document"></i>
-                            <span>{{ formData.word_file_name }}</span>
+                            <span>{{ formData.word_file_name || '已上传Word文档' }}</span>
                             <el-button type="text" @click="removeWordFile" size="mini">删除</el-button>
                         </div>
                     </div>
 
                     <!-- 图片上传 -->
-                    <div v-else-if="formData.content_type === 3" class="image-upload-container">
-                        <uploadImage :limit="1" v-model="formData.image_url" @upload-success="onImageUploadSuccess">
+                    <div v-else-if="formData.show_type === 3" class="image-upload-container">
+                        <uploadImage :limit="1" v-model="formData.file_url" @upload-success="onImageUploadSuccess">
                         </uploadImage>
                     </div>
                 </el-form-item>
@@ -90,17 +89,16 @@ export default {
                 type: 21, // 固定为复习资料类型
                 cate_id: null,
                 content: "",
-                content_type: 1, // 内容类型:1-富文本,2-Word文档,3-图片
+                show_type: 1, // 内容类型:1-默认编辑器,2-Word文档,3-图片
+                file_url: "", // 上传的文档或图片URL
                 word_file: null, // Word文档文件对象
-                word_file_name: "", // Word文档名称
-                image_url: "" // 图片URL
+                word_file_name: "" // Word文档名称
             },
             categoryOptions: [],
             formRules: {
                 title: [{ required: true, message: "请输入标题", trigger: "blur" }],
                 cate_id: [{ required: true, message: "请选择分类", trigger: "change" }],
-                content_type: [{ required: true, message: "请选择内容类型", trigger: "change" }],
-                content: [{ required: true, message: "请输入内容", trigger: "blur" }]
+                show_type: [{ required: true, message: "请选择内容类型", trigger: "change" }]
             },
             editorConfig: {
                 height: 400,
@@ -191,10 +189,10 @@ export default {
                         type: 21,
                         cate_id: null,
                         content: "",
-                        content_type: 1,
+                        show_type: 1,
+                        file_url: "",
                         word_file: null,
-                        word_file_name: "",
-                        image_url: ""
+                        word_file_name: ""
                     };
                     this.loadCategories();
                 }
@@ -212,16 +210,23 @@ export default {
                 await this.loadCategories();
                 console.log('Category options loaded:', this.categoryOptions);
                 // 然后设置表单数据
+                // 从file_url提取文件名
+                let wordFileName = '';
+                if (data.file_url) {
+                    const urlParts = data.file_url.split('/');
+                    wordFileName = urlParts[urlParts.length - 1] || '';
+                }
+
                 this.formData = {
                     id: data.id,
                     title: data.title,
                     type: 21,
                     cate_id: data.cate_id || null,
                     content: data.content || "",
-                    content_type: data.content_type || 1,
+                    show_type: data.show_type || 1,
+                    file_url: data.file_url || "",
                     word_file: null, // 编辑时不加载文件对象
-                    word_file_name: data.word_file_name || "",
-                    image_url: data.image_url || ""
+                    word_file_name: wordFileName
                 };
                 console.log('Form data set:', this.formData);
             } else {
@@ -233,6 +238,26 @@ export default {
                 if (!valid) return;
                 this.saving = true;
                 try {
+                    // 处理Word文档上传
+                    let fileUrl = this.formData.file_url;
+                    if (this.formData.show_type === 2 && this.formData.word_file) {
+                        // 上传Word文档并获取URL
+                        const formData = new FormData();
+                        formData.append('file', this.formData.word_file);
+                        try {
+                            const uploadRes = await this.$http.post('/upload/uploadImage', formData, {
+                                headers: { 'Content-Type': 'multipart/form-data' }
+                            });
+                            if (uploadRes.data.code === 0) {
+                                fileUrl = uploadRes.data.data.url || uploadRes.data.data;
+                            }
+                        } catch (uploadError) {
+                            console.error('文件上传失败:', uploadError);
+                            this.$message.error('Word文档上传失败,请重试');
+                            return;
+                        }
+                    }
+
                     // 只提交需要的字段
                     const submitData = {
                         id: this.formData.id,
@@ -240,6 +265,8 @@ export default {
                         type: 21,
                         cate_id: this.formData.cate_id,
                         content: this.formData.content,
+                        show_type: this.formData.show_type,
+                        file_url: fileUrl,
                         status: 1, // 默认发布状态
                         sort: 0, // 默认排序
                         author: '', // 默认作者
@@ -281,26 +308,10 @@ export default {
 
         // 内容类型变化处理
         onContentTypeChange(type) {
-            // 切换内容类型时清空其他类型的数据,但保留已解析的内容
-            if (type === 1) {
-                // 富文本编辑 - 不清空content,保留已解析的内容
-                this.formData.word_file = null;
-                this.formData.word_file_name = '';
-                this.formData.image_url = '';
-            } else if (type === 2) {
-                // Word文档 - 如果还没有解析内容,则清空content
-                if (!this.formData.word_file) {
-                    this.formData.content = '';
-                }
-                this.formData.image_url = '';
-            } else if (type === 3) {
-                // 图片 - 如果还没有解析内容,则清空content
-                if (!this.formData.image_url) {
-                    this.formData.content = '';
-                }
-                this.formData.word_file = null;
-                this.formData.word_file_name = '';
-            }
+            // 只清空临时文件对象,保留原有的 file_url 和 content
+            // 这样在编辑模式下切换类型时,不会丢失原有的链接
+            this.formData.word_file = null;
+            this.formData.word_file_name = '';
         },
 
         // Word文档上传前验证
@@ -311,7 +322,7 @@ export default {
                 this.$message.error('只能上传Word文档!');
                 return false;
             }
-            const isLt10M = file.size / 1024 / 1024 < 10;
+            const isLt10M = file.size / 1024 / 1024 <= 10;
             if (!isLt10M) {
                 this.$message.error('Word文档大小不能超过 10MB!');
                 return false;
@@ -325,10 +336,7 @@ export default {
             if (file.raw) {
                 this.formData.word_file = file.raw;
                 this.formData.word_file_name = file.name;
-                this.$message.success('Word文档选择成功');
-
-                // 直接解析Word文档内容
-                this.parseWordContentFromFile(file.raw);
+                this.$message.success('Word文档选择成功,保存时将上传文件');
             }
         },
 
@@ -336,6 +344,13 @@ export default {
         removeWordFile() {
             this.formData.word_file = null;
             this.formData.word_file_name = '';
+            // 编辑模式下不清空 file_url,保留原有的链接
+            // 新增模式下,如果没有数据库中的 file_url(只是临时选择的文件),可以清空
+            if (!this.isEdit && !this.formData.id) {
+                // 新增模式且没有ID,清空file_url
+                this.formData.file_url = '';
+            }
+            // 编辑模式或有ID的情况下,保留原有的 file_url
         },
 
         // 图片上传前验证
@@ -355,10 +370,8 @@ export default {
 
         // 图片上传成功
         onImageUploadSuccess(uploadData) {
+            this.formData.file_url = uploadData.url || uploadData.data?.url || uploadData.data;
             this.$message.success('图片上传成功');
-
-            // 将图片内容显示在富文本编辑器中
-            this.parseImageContent(uploadData.url, uploadData.fileName || '上传的图片');
         },
 
         // 图片上传失败
@@ -368,90 +381,9 @@ export default {
 
         // 删除图片
         removeImage() {
-            this.formData.image_url = '';
-        },
-
-        // 解析Word文档内容(从文件对象)
-        async parseWordContentFromFile(file) {
-            try {
-                // 切换到富文本编辑模式
-                this.formData.content_type = 1;
-
-                // 使用前端解析Word文档
-                const content = await this.parseWordWithFrontendFromFile(file);
-
-                // 将解析的内容设置到富文本编辑器中
-                this.formData.content = content;
-                this.$message.success('Word文档内容解析成功,已加载到编辑器中');
-            } catch (error) {
-                console.error('解析Word文档失败:', error);
-                // 解析失败时的备用内容
-                this.formData.content = `<div class="word-document">
-                    <h3>Word文档:${file.name}</h3>
-                    <p>文件大小:${(file.size / 1024 / 1024).toFixed(2)} MB</p>
-                    <p>上传时间:${new Date().toLocaleString()}</p>
-                    <p>注意:Word文档内容解析失败,请手动编辑内容</p>
-                </div>`;
-                this.$message.warning('Word文档内容解析失败,请手动编辑内容');
-            }
-        },
-
-        // 前端解析Word文档(从文件对象)
-        async parseWordWithFrontendFromFile(file) {
-            try {
-                // 导入mammoth
-                const mammoth = require('mammoth');
-
-                // 读取文件内容
-                const arrayBuffer = await file.arrayBuffer();
-
-                // 使用mammoth解析Word文档
-                const result = await mammoth.convertToHtml({ arrayBuffer: arrayBuffer });
-
-                let content = '';
-
-                if (result.value) {
-                    // 获取解析后的HTML内容
-                    content = '<div class="word-content">' + result.value + '</div>';
-
-                    // 如果有警告信息,在控制台显示
-                    if (result.messages && result.messages.length > 0) {
-                        console.log('Word文档解析警告:', result.messages);
-                    }
-
-                    this.$message.success('Word文档解析成功');
-                } else {
-                    content = '<div class="word-content"><p>Word文档解析失败,请手动输入或粘贴内容。</p></div>';
-                    this.$message.warning('Word文档解析失败,请手动输入内容');
-                }
-
-                return content;
-            } catch (error) {
-                console.error('前端解析Word文档失败:', error);
-                this.$message.error('Word文档解析失败: ' + error.message);
-                return '<div class="word-content"><p>Word文档解析失败,请手动输入内容。</p></div>';
-            }
+            this.formData.file_url = '';
         },
 
-        // HTML转义函数
-        escapeHtml(text) {
-            const div = document.createElement('div');
-            div.textContent = text;
-            return div.innerHTML;
-        },
-
-        // 解析图片内容
-        parseImageContent(url, fileName) {
-            // 切换到富文本编辑模式
-            this.formData.content_type = 1;
-
-            // 将图片内容设置到富文本编辑器中
-            this.formData.content = `<div class="image-content">
-                <img src="${url}" alt="${fileName}" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px; margin: 10px 0;">
-            </div>`;
-
-            this.$message.success('图片已加载到编辑器中,您可以继续编辑');
-        }
     }
 };
 </script>
@@ -506,6 +438,11 @@ export default {
     background-color: #fafafa;
 }
 
+.image-preview {
+    margin-top: 15px;
+    text-align: center;
+}
+
 .image-uploader {
     display: inline-block;
 }

+ 285 - 0
addons/admin/src/views/system/institution/index.vue

@@ -0,0 +1,285 @@
+<template>
+    <div class="ele-body">
+        <el-card shadow="never">
+            <!-- 搜索表单 -->
+            <el-form :model="table.where" label-width="90px" class="ele-form-search"
+                @keyup.enter.native="$refs.table.reload()" @submit.native.prevent>
+                <el-row :gutter="15">
+                    <el-col :md="6" :sm="12">
+                        <el-form-item label="院校名称:">
+                            <el-input v-model="table.where.keyword" placeholder="请输入院校名称或代码" clearable />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="6" :sm="12">
+                        <el-form-item label="状态:">
+                            <el-select v-model="table.where.status" placeholder="请选择状态" clearable class="ele-fluid">
+                                <el-option label="正常" :value="1" />
+                                <el-option label="不显示" :value="2" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="6" :sm="12">
+                        <div class="ele-form-actions">
+                            <el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search"
+                                class="ele-btn-icon">查询</el-button>
+                            <el-button @click="resetSearch">重置</el-button>
+                        </div>
+                    </el-col>
+                </el-row>
+            </el-form>
+
+            <!-- 操作按钮 -->
+            <div class="ele-table-tool ele-table-tool-default">
+                <el-button @click="showEdit = true" type="primary" icon="el-icon-plus" class="ele-btn-icon"
+                    size="small">添加院校</el-button>
+                <el-button @click="remove()" type="danger" icon="el-icon-delete" class="ele-btn-icon"
+                    size="small">批量删除</el-button>
+            </div>
+
+            <!-- 数据表格 -->
+            <ele-data-table ref="table" :config="table" :choose.sync="choose" height="calc(100vh - 315px)"
+                highlight-current-row>
+                <template slot-scope="{index}">
+                    <el-table-column type="selection" width="45" align="center" fixed="left" />
+                    <el-table-column type="index" :index="index" label="编号" width="60" align="center" fixed="left"
+                        show-overflow-tooltip />
+
+                    <el-table-column label="封面" width="100" align="center">
+                        <template slot-scope="{row}">
+                            <el-avatar shape="square" :size="50" :src="row.thumb" v-if="row.thumb" />
+                            <span v-else class="ele-text-secondary">无封面</span>
+                        </template>
+                    </el-table-column>
+
+                    <el-table-column prop="name" label="院校名称" sortable="custom" show-overflow-tooltip min-width="200" />
+                    <el-table-column prop="code" label="院校代码" sortable="custom" show-overflow-tooltip min-width="120" />
+
+                    <el-table-column prop="link_type_text" label="跳转类型" width="120" align="center" />
+                    <el-table-column prop="link_url" label="详情地址" show-overflow-tooltip min-width="200" align="left" />
+
+                    <el-table-column prop="description" label="简介" show-overflow-tooltip min-width="250">
+                        <template slot-scope="{row}">
+                            <text-ellipsis :text="row.description || '暂无简介'" :max-length="50" />
+                        </template>
+                    </el-table-column>
+
+                    <el-table-column prop="sort" label="排序" sortable="custom" show-overflow-tooltip width="80"
+                        align="center" />
+
+                    <el-table-column prop="status_text" label="状态" width="100" align="center">
+                        <template slot-scope="{row}">
+                            <ele-dot :type="row.status === 1 ? 'success' : 'danger'" :ripple="row.status === 1"
+                                :text="row.status_text" />
+                        </template>
+                    </el-table-column>
+
+                    <el-table-column label="创建时间" sortable="custom" show-overflow-tooltip min-width="160">
+                        <template slot-scope="{row}">{{ row.create_time }}</template>
+                    </el-table-column>
+
+                    <el-table-column label="操作" width="180" fixed="right" align="center">
+                        <template slot-scope="{row}">
+                            <el-link @click="edit(row)" icon="el-icon-edit" type="primary"
+                                :underline="false">修改</el-link>
+                            <el-link @click="editStatus(row)" icon="el-icon-check" type="success" :underline="false"
+                                v-if="row.status === 2">启用</el-link>
+                            <el-link @click="editStatus(row)" icon="el-icon-close" type="warning" :underline="false"
+                                v-if="row.status === 1">禁用</el-link>
+                            <el-popconfirm title="确定要删除此院校吗?" @confirm="remove(row)" class="ele-action">
+                                <el-link slot="reference" icon="el-icon-delete" type="danger"
+                                    :underline="false">删除</el-link>
+                            </el-popconfirm>
+                        </template>
+                    </el-table-column>
+                </template>
+            </ele-data-table>
+        </el-card>
+
+        <!-- 编辑弹窗 -->
+        <el-dialog :title="editForm.id ? '修改院校' : '添加院校'" :visible.sync="showEdit" width="700px" @closed="resetForm"
+            :destroy-on-close="true" custom-class="ele-dialog-form" :lock-scroll="false">
+            <el-form :model="editForm" ref="editForm" :rules="editRules" label-width="100px">
+                <el-form-item label="封面图片:">
+                    <uploadImage :limit="1" v-model="editForm.thumb"></uploadImage>
+                </el-form-item>
+
+                <el-row :gutter="15">
+                    <el-col :sm="12">
+                        <el-form-item label="院校名称:" prop="name">
+                            <el-input v-model="editForm.name" placeholder="请输入院校名称" clearable />
+                        </el-form-item>
+                    </el-col>
+                    <el-col :sm="12">
+                        <el-form-item label="院校代码:" prop="code">
+                            <el-input v-model="editForm.code" placeholder="请输入院校代码" clearable />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+
+                <el-row :gutter="15">
+                    <el-col :sm="12">
+                        <el-form-item label="跳转类型:" prop="link_type">
+                            <el-select v-model="editForm.link_type" placeholder="请选择跳转类型" class="ele-fluid">
+                                <el-option label="Web链接" :value="1" />
+                                <el-option label="小程序链接" :value="2" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :sm="12">
+                        <el-form-item label="排序号:" prop="sort">
+                            <el-input-number v-model="editForm.sort" controls-position="right" :min="0"
+                                placeholder="排序号" class="ele-fluid ele-text-left" />
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+
+                <el-form-item label="详情地址:" prop="link_url">
+                    <el-input v-model="editForm.link_url" placeholder="请输入详情地址(小程序需填写appID)" clearable />
+                </el-form-item>
+
+                <el-form-item label="小程序APPID:" v-if="editForm.link_type === 2" prop="mp_appid">
+                    <el-input v-model="editForm.mp_appid" placeholder="请输入小程序APPID" clearable />
+                </el-form-item>
+
+                <el-form-item label="简介:">
+                    <el-input type="textarea" :rows="3" v-model="editForm.description" placeholder="请输入院校简介"
+                        clearable />
+                </el-form-item>
+
+                <el-form-item label="状态:">
+                    <el-radio-group v-model="editForm.status">
+                        <el-radio :label="1">正常</el-radio>
+                        <el-radio :label="2">不显示</el-radio>
+                    </el-radio-group>
+                </el-form-item>
+            </el-form>
+            <div slot="footer">
+                <el-button @click="showEdit = false">取消</el-button>
+                <el-button type="primary" @click="save">保存</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import uploadImage from '@/components/uploadImage'
+export default {
+    name: "SysInstitution",
+    components: { uploadImage },
+    data() {
+        return {
+            table: { url: '/institutions/index', where: {} },  // 表格配置
+            choose: [],  // 表格选中数据
+            showEdit: false,  // 是否显示表单弹窗
+            editForm: { status: 1, link_type: 1, sort: 0 },  // 表单数据
+            editRules: {  // 表单验证规则
+                name: [
+                    { required: true, message: '请输入院校名称', trigger: 'blur' }
+                ],
+                code: [
+                    { required: true, message: '请输入院校代码', trigger: 'blur' }
+                ]
+            }
+        }
+    },
+    mounted() {
+
+    },
+    methods: {
+        /* 重置搜索 */
+        resetSearch() {
+            this.table.where = {};
+            this.$refs.table.reload();
+        },
+        /* 显示编辑 */
+        edit(row) {
+            this.editForm = Object.assign({ status: 1, link_type: 1, sort: 0 }, row);
+            this.showEdit = true;
+        },
+        /* 重置表单 */
+        resetForm() {
+            this.editForm = { status: 1, link_type: 1, sort: 0 };
+        },
+        /* 保存编辑 */
+        save() {
+            this.$refs['editForm'].validate((valid) => {
+                if (valid) {
+                    const loading = this.$loading({ lock: true });
+                    this.$http.post('/institutions/edit', this.editForm).then(res => {
+                        loading.close();
+                        if (res.data.code === 0) {
+                            this.showEdit = false;
+                            this.$message({ type: 'success', message: res.data.msg });
+                            this.$refs.table.reload();
+                        } else {
+                            this.$message.error(res.data.msg);
+                        }
+                    }).catch(e => {
+                        loading.close();
+                        this.$message.error(e.message);
+                    });
+                } else {
+                    return false;
+                }
+            });
+        },
+        /* 修改状态 */
+        editStatus(row) {
+            const status = row.status === 1 ? 2 : 1;
+            this.$http.post('/institutions/status', { id: row.id, status: status }).then(res => {
+                if (res.data.code === 0) {
+                    this.$message({ type: 'success', message: res.data.msg });
+                    this.$refs.table.reload();
+                } else {
+                    this.$message.error(res.data.msg);
+                }
+            }).catch(e => {
+                this.$message.error(e.message);
+            });
+        },
+        /* 删除 */
+        remove(row) {
+            if (!row) {  // 批量删除
+                if (this.choose.length === 0) return this.$message.error('请至少选择一条数据');
+                let ids = this.choose.map(d => d.id);
+                this.$confirm('确定要删除选中的院校吗?', '提示', { type: 'warning' }).then(() => {
+                    const loading = this.$loading({ lock: true });
+                    this.$http.post('/institutions/delete', { id: ids }).then(res => {
+                        loading.close();
+                        if (res.data.code === 0) {
+                            this.$message({ type: 'success', message: res.data.msg });
+                            this.$refs.table.reload();
+                        } else {
+                            this.$message.error(res.data.msg);
+                        }
+                    }).catch(e => {
+                        loading.close();
+                        this.$message.error(e.message);
+                    });
+                }).catch(() => 0);
+            } else {  // 单个删除
+                const loading = this.$loading({ lock: true });
+                this.$http.post('/institutions/delete', { id: row.id }).then(res => {
+                    loading.close();
+                    if (res.data.code === 0) {
+                        this.$message({ type: 'success', message: res.data.msg });
+                        this.$refs.table.reload();
+                    } else {
+                        this.$message.error(res.data.msg);
+                    }
+                }).catch(e => {
+                    loading.close();
+                    this.$message.error(e.message);
+                });
+            }
+        }
+    }
+}
+</script>
+
+<style scoped>
+.ele-block>>>.el-upload,
+.ele-block>>>.el-upload-dragger {
+    width: 100%;
+}
+</style>

+ 37 - 55
app/Helpers/common.php

@@ -322,7 +322,6 @@ if (!function_exists('datetime')) {
         $time = is_numeric($time) ? $time : strtotime($time);
         return date($format, $time);
     }
-
 }
 
 if (!function_exists('dateForWeek')) {
@@ -573,17 +572,17 @@ if (!function_exists('format_times')) {
      * @author laravel开发员
      * @date 2019/5/23
      */
-    function format_times($time, $type=0)
+    function format_times($time, $type = 0)
     {
-        if($type == 1){
-            $hour = $time>=3600? intval($time/3600) : 0;
-            $minute = $time>=60? intval($time%3600/60) : 0;
-            $second = intval($time%60);
-            return ($hour<10?'0'.$hour:$hour).':'.($minute<10?'0'.$minute:$minute).':'.($second<10?'0'.$second:$second);
-        }else{
-            $minute = $time>=60? intval($time/60) : 0;
-            $second = intval($time%60);
-            return ($minute<10?'0'.$minute:$minute).':'.($second<10?'0'.$second:$second);
+        if ($type == 1) {
+            $hour = $time >= 3600 ? intval($time / 3600) : 0;
+            $minute = $time >= 60 ? intval($time % 3600 / 60) : 0;
+            $second = intval($time % 60);
+            return ($hour < 10 ? '0' . $hour : $hour) . ':' . ($minute < 10 ? '0' . $minute : $minute) . ':' . ($second < 10 ? '0' . $second : $second);
+        } else {
+            $minute = $time >= 60 ? intval($time / 60) : 0;
+            $second = intval($time % 60);
+            return ($minute < 10 ? '0' . $minute : $minute) . ':' . ($second < 10 ? '0' . $second : $second);
         }
     }
 }
@@ -606,7 +605,6 @@ if (!function_exists('format_bytes')) {
         }
         return round($size, 2) . $delimiter . $units[$i];
     }
-
 }
 
 if (!function_exists('format_yuan')) {
@@ -625,7 +623,6 @@ if (!function_exists('format_yuan')) {
         }
         return "0.00";
     }
-
 }
 
 if (!function_exists('format_cent')) {
@@ -637,9 +634,8 @@ if (!function_exists('format_cent')) {
      */
     function format_cent($money)
     {
-        return ($money * 100).'';
+        return ($money * 100) . '';
     }
-
 }
 
 if (!function_exists('format_day')) {
@@ -655,13 +651,12 @@ if (!function_exists('format_day')) {
     {
         if ($day >= 365) {
             return '一年';
-        }else if($day>=30){
-            return intval($day/30).'个月';
-        }else{
-            return intval($day).'天';
+        } else if ($day >= 30) {
+            return intval($day / 30) . '个月';
+        } else {
+            return intval($day) . '天';
         }
     }
-
 }
 
 if (!function_exists('format_bank_card')) {
@@ -692,7 +687,6 @@ if (!function_exists('format_bank_card')) {
         }
         return $format_card_no;
     }
-
 }
 
 if (!function_exists('format_mobile')) {
@@ -760,7 +754,6 @@ if (!function_exists('get_password')) {
     {
         return md5(md5($password));
     }
-
 }
 
 if (!function_exists('get_order_num')) {
@@ -777,7 +770,6 @@ if (!function_exists('get_order_num')) {
         $micro = substr(microtime(), 2, 2);
         return $prefix . date("YmdHis") . $micro . rand(1000, 9999);
     }
-
 }
 
 if (!function_exists('getter')) {
@@ -839,7 +831,6 @@ if (!function_exists('get_zodiac_sign')) {
         }
         return $sign_name;
     }
-
 }
 
 if (!function_exists('get_image_url')) {
@@ -1039,7 +1030,7 @@ if (!function_exists('format_content')) {
             return false;
         }
 
-        $content = str_replace(["\n",'\n'], ["<br/>","<br/>"], htmlspecialchars_decode($content));
+        $content = str_replace(["\n", '\n'], ["<br/>", "<br/>"], htmlspecialchars_decode($content));
         return get_format_content($content);
     }
 }
@@ -1123,7 +1114,6 @@ if (!function_exists('get_client_ip')) {
         $ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
         return $ip[$type];
     }
-
 }
 
 if (!function_exists('get_guid_v4')) {
@@ -1150,7 +1140,7 @@ if (!function_exists('get_guid_v4')) {
             return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
         }
         // Fallback (PHP 4.2+)
-        mt_srand((double) microtime() * 10000);
+        mt_srand((float) microtime() * 10000);
         $charid = strtolower(md5(uniqid(rand(), true)));
         $hyphen = chr(45);                  // "-"
         $lbrace = $trim ? "" : chr(123);    // "{"
@@ -1179,7 +1169,6 @@ if (!function_exists('is_email')) {
     {
         return preg_match('/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/', $str);
     }
-
 }
 
 if (!function_exists('is_mobile')) {
@@ -1195,7 +1184,6 @@ if (!function_exists('is_mobile')) {
     {
         return preg_match('/^1(3|4|5|7|8)\d{9}$/', $num);
     }
-
 }
 
 if (!function_exists('is_zipcode')) {
@@ -1211,7 +1199,6 @@ if (!function_exists('is_zipcode')) {
     {
         return preg_match('/^[1-9][0-9]{5}$/', $code);
     }
-
 }
 
 if (!function_exists('is_idcard')) {
@@ -1270,7 +1257,6 @@ if (!function_exists('is_idcard')) {
             }
         }
     }
-
 }
 
 if (!function_exists('is_empty')) {
@@ -1420,7 +1406,7 @@ if (!function_exists('message')) {
      */
     function message($msg = "操作成功", $success = true, $data = [], $code = 0, $type = 'json')
     {
-        $result = ['success' => $success, 'msg' => lang($msg), 'data' => $success==true || env('APP_DEBUG')?$data:[], 'stime' => time()];
+        $result = ['success' => $success, 'msg' => lang($msg), 'data' => $success == true || env('APP_DEBUG') ? $data : [], 'stime' => time()];
         if ($success) {
             $result['code'] = 0;
         } else {
@@ -1586,7 +1572,6 @@ if (!function_exists('strip_html_tags')) {
         }
         return $str;
     }
-
 }
 
 if (!function_exists('sub_str')) {
@@ -1619,7 +1604,6 @@ if (!function_exists('sub_str')) {
         $omit = mb_strlen($str) >= $length ? '...' : '';
         return $suffix ? $slice . $omit : $slice;
     }
-
 }
 
 if (!function_exists('save_image')) {
@@ -1809,13 +1793,13 @@ if (!function_exists('upload_image')) {
         $size = $file->getSize();
 
         // 文件大小校验
-        if ($size > 5 * 1024 * 1024) {
-            return message("文件大小超过了5M", false);
+        if ($size > 10 * 1024 * 1024) {
+            return message("文件大小超过了10M", false);
         }
 
         // 文件后缀校验
         $ext = strtolower($ext);
-        $ext_arr = array('jpg', 'jpeg', 'png', 'gif');
+        $ext_arr = array('jpg', 'jpeg', 'png', 'gif', 'doc', 'docx');
         if (!in_array($ext, $ext_arr)) {
             return message("文件格式不正确:" . $ext, false);
         }
@@ -2176,7 +2160,6 @@ if (!function_exists('httpRequest')) {
                 $ret = iconv('gb2312', 'utf-8', $ret);
                 $retArr = json_decode($ret, true);
             }
-
         } catch (\Exception $exception) {
             $retArr = ['code' => 'error', 'msg' => 'request error'];
         }
@@ -2199,20 +2182,20 @@ if (!function_exists('aiRequest')) {
     function aiRequest($url, $data = '', $timeout = 60, $header = [])
     {
 
-            set_time_limit($timeout);
-            $ch = curl_init($url);
-            if ($header) {
-                curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
-            }
-            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
-            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
-            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);    //禁止 cURL 验证对等证书
-            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);    //是否检测服务器的域名与证书上的是否一致
-            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
-            $ret = curl_exec($ch);
-            curl_close($ch);
-            return $ret;
+        set_time_limit($timeout);
+        $ch = curl_init($url);
+        if ($header) {
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+        }
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);    //禁止 cURL 验证对等证书
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);    //是否检测服务器的域名与证书上的是否一致
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+        $ret = curl_exec($ch);
+        curl_close($ch);
+        return $ret;
     }
 }
 
@@ -2317,7 +2300,6 @@ if (!function_exists('getDistance')) {
                 )
             )
         ) * 1000);
-
     }
 }
 
@@ -2408,7 +2390,7 @@ if (!function_exists('crypt_answer')) {
         }
 
         $key = $key ? $key : env('APP_SIGN_KEY', '');
-        return md5($id.'-'. $answer.'-'.$key);
+        return md5($id . '-' . $answer . '-' . $key);
     }
 }
 
@@ -2473,4 +2455,4 @@ if (!function_exists('format_image_field')) {
         // 单图字符串
         return get_image_url($images, $domain);
     }
-}
+}

+ 41 - 0
app/Http/Controllers/Admin/InstitutionController.php

@@ -52,6 +52,47 @@ class InstitutionController extends Backend
     }
 
     /**
+     * 详情
+     * @return array
+     */
+    public function info()
+    {
+        $id = request()->input("id", 0);
+        $info = [];
+        if ($id) {
+            $info = $this->service->getInfo($id);
+        }
+        return message(MESSAGE_OK, true, $info);
+    }
+
+    /**
+     * 添加或编辑
+     * @return array
+     */
+    public function edit()
+    {
+        return $this->service->edit();
+    }
+
+    /**
+     * 删除
+     * @return array
+     */
+    public function delete()
+    {
+        return $this->service->delete();
+    }
+
+    /**
+     * 修改状态
+     * @return array
+     */
+    public function status()
+    {
+        return $this->service->status();
+    }
+
+    /**
      * 选项列表
      * @return mixed
      */

+ 5 - 0
app/Models/InstitutionModel.php

@@ -50,6 +50,11 @@ class InstitutionModel extends BaseModel
 
     public function getThumbAttribute($value)
     {
+        return $value ? get_image_url($value) : '';
+    }
 
+    public function setThumbAttribute($value)
+    {
+        return $value ? get_image_path($value) : '';
     }
 }

+ 14 - 0
app/Services/Common/ArticleService.php

@@ -102,6 +102,10 @@ class ArticleService extends BaseService
                 $item['create_time'] = $item['create_time'] ? datetime($item['create_time'], 'Y-m-d H.i.s') : '';
                 $item['cover'] = $item['cover'] ? get_image_url($item['cover']) : '';
                 $item['content'] = $item['content'] ? get_format_content($item['content']) : '';
+                // 处理file_url,转换为完整URL
+                if (isset($item['file_url']) && $item['file_url']) {
+                    $item['file_url'] = get_image_url($item['file_url']);
+                }
             }
         }
 
@@ -127,6 +131,12 @@ class ArticleService extends BaseService
             $data['cover'] = get_image_path(trim($data['cover']));
         }
 
+        // 处理文件URL(Word文档或图片)
+        if (!empty($data['file_url'])) {
+            $data['file_url'] = get_image_path(trim($data['file_url']));
+        }
+
+        // 处理内容
         $content = isset($data['content']) ? $data['content'] : '';
         if ($content) {
             $data['content'] = set_format_content($content);
@@ -228,6 +238,10 @@ class ArticleService extends BaseService
             if (isset($info['content'])) {
                 $info['content'] = $info['content'] ? get_format_content($info['content']) : '';
             }
+            // 确保file_url返回完整URL
+            if (isset($info['file_url'])) {
+                $info['file_url'] = $info['file_url'] ? get_image_url($info['file_url']) : '';
+            }
         }
         return message(MESSAGE_OK, true, $info);
     }

+ 30 - 0
app/Services/Common/InstitutionService.php

@@ -142,6 +142,11 @@ class InstitutionService extends BaseService
             return message('院校代码已存在', false);
         }
 
+        // 处理封面图片
+        if (!empty($data['thumb'])) {
+            $data['thumb'] = get_image_path($data['thumb']);
+        }
+
         // 确保所有必填字段都存在
         $data = [
             'name' => $data['name'] ?? '',
@@ -149,6 +154,7 @@ class InstitutionService extends BaseService
             'thumb' => $data['thumb'] ?? '',
             'link_url' => $data['link_url'] ?? '',
             'link_type' => $data['link_type'] ?? 1,
+            'mp_appid' => $data['mp_appid'] ?? '',
             'description' => $data['description'] ?? '',
             'sort' => $data['sort'] ?? 0,
             'status' => $data['status'] ?? 1,
@@ -158,6 +164,21 @@ class InstitutionService extends BaseService
     }
 
     /**
+     * 获取记录详情
+     * @return array
+     */
+    public function info()
+    {
+        // 记录ID
+        $id = request()->input("id", 0);
+        $info = [];
+        if ($id) {
+            $info = $this->model->getInfo($id);
+        }
+        return message(MESSAGE_OK, true, $info);
+    }
+
+    /**
      * 删除院校
      * @return array
      */
@@ -173,6 +194,15 @@ class InstitutionService extends BaseService
     }
 
     /**
+     * 设置状态
+     * @return array
+     */
+    public function status()
+    {
+        return parent::status();
+    }
+
+    /**
      * 院校选项
      * @return array
      */