Explorar o código

添加直达院校管理功能

罗永浩 hai 6 meses
pai
achega
9fe3ebdb5f

+ 1 - 1
.env

@@ -39,7 +39,7 @@ SESSION_LIFETIME=120
 
 REDIS_PREFIX=null
 REDIS_HOST=127.0.0.1
-REDIS_PASSWORD=derkj&6688
+REDIS_PASSWORD=
 REDIS_PORT=16379
 REDIS_DB=1
 

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

@@ -0,0 +1,292 @@
+<template>
+    <div class="ele-body">
+        <el-card shadow="never">
+            <!-- 搜索表单 -->
+            <el-form :model="table.where" label-width="100px" 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.link_type" placeholder="请选择链接类型" clearable>
+                                <el-option label="Web链接" :value="1" />
+                                <el-option label="小程序链接" :value="2" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="6" :sm="12">
+                        <el-form-item label="状态">
+                            <el-select v-model="table.where.status" placeholder="请选择状态" clearable>
+                                <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 type="primary" v-if="permission.includes(permissionMap['add'])" icon="el-icon-plus"
+                    @click="openForm()">
+                    新增院校
+                </el-button>
+                <el-button type="danger" v-if="permission.includes(permissionMap['delete'])" icon="el-icon-delete"
+                    @click="batchRemove()">
+                    批量删除
+                </el-button>
+            </div>
+
+            <!-- 数据表格 -->
+            <ele-data-table ref="table" :config="table" :choose.sync="choose" height="calc(100vh - 280px)"
+                highlight-current-row>
+                <template slot-scope="{ row }">
+                    <el-table-column type="selection" width="45" align="center" fixed="left" />
+                    <el-table-column type="index" label="编号" width="60" align="center" fixed="left" />
+                    <el-table-column prop="name" label="院校名称" min-width="150" />
+                    <el-table-column prop="code" label="院校代码" width="120" />
+                    <el-table-column prop="thumb" label="封面/Logo" width="100">
+                        <template slot-scope="{ row }">
+                            <el-image v-if="row.thumb" :src="row.thumb" style="width: 50px; height: 50px;"
+                                fit="cover" />
+                            <span v-else>-</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="link_type" label="链接类型" width="100">
+                        <template slot-scope="{ row }">
+                            <el-tag :type="row.link_type === 1 ? 'primary' : 'success'">
+                                {{ row.link_type === 1 ? 'Web链接' : '小程序链接' }}
+                            </el-tag>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="link_url" label="详情地址" min-width="200" show-overflow-tooltip>
+                        <template slot-scope="{ row }">
+                            <el-link v-if="row.link_url" :href="row.link_url" target="_blank" type="primary">
+                                {{ row.link_url }}
+                            </el-link>
+                            <span v-else>-</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="description" label="介绍" min-width="150" show-overflow-tooltip />
+                    <el-table-column prop="sort" label="排序" width="80" align="center">
+                        <template slot-scope="{ row }">
+                            <el-input-number v-model="row.sort" :min="0" size="mini" @change="updateSort(row)" />
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="状态" width="80">
+                        <template slot-scope="{ row }">
+                            <el-switch v-model="row.status" :active-value="1" :inactive-value="2"
+                                @change="editStatus(row)" />
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="create_time" label="创建时间" width="160" />
+                    <el-table-column label="操作" width="160" fixed="right">
+                        <template slot-scope="{ row }">
+                            <el-button v-if="permission.includes(permissionMap['edit'])" type="text" size="mini"
+                                @click="openForm(row)">编辑</el-button>
+                            <el-button v-if="permission.includes(permissionMap['delete'])" type="text" size="mini"
+                                @click="remove(row)">删除</el-button>
+                        </template>
+                    </el-table-column>
+                </template>
+            </ele-data-table>
+        </el-card>
+
+        <!-- 新增/编辑弹窗 -->
+        <el-dialog :title="isEdit ? '编辑院校' : '新增院校'" :visible.sync="formVisible" width="600px">
+            <el-form :model="formData" :rules="formRules" ref="formRef" label-width="120px">
+                <el-form-item label="院校名称" prop="name">
+                    <el-input v-model="formData.name" placeholder="请输入院校名称" />
+                </el-form-item>
+                <el-form-item label="院校代码" prop="code">
+                    <el-input v-model="formData.code" placeholder="请输入院校代码" />
+                </el-form-item>
+                <el-form-item label="封面/Logo">
+                    <uploadImage :limit="1" v-model="formData.thumb"></uploadImage>
+                </el-form-item>
+                <el-form-item label="链接类型" prop="link_type">
+                    <el-select v-model="formData.link_type" placeholder="请选择链接类型">
+                        <el-option label="Web链接" :value="1" />
+                        <el-option label="小程序链接" :value="2" />
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="详情地址" prop="link_url">
+                    <el-input v-model="formData.link_url" placeholder="请输入详情地址" />
+                </el-form-item>
+                <el-form-item label="介绍">
+                    <el-input v-model="formData.description" type="textarea" placeholder="请输入院校介绍" rows="3" />
+                </el-form-item>
+                <el-form-item label="排序">
+                    <el-input-number v-model="formData.sort" :min="0" />
+                </el-form-item>
+                <el-form-item label="状态">
+                    <el-switch v-model="formData.status" :active-value="1" :inactive-value="2" />
+                </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click="formVisible = false">取消</el-button>
+                <el-button type="primary" @click="saveForm">保存</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import uploadImage from '@/components/uploadImage.vue'
+
+export default {
+    name: "InstitutionPage",
+    components: {
+        uploadImage
+    },
+    data() {
+        return {
+            table: {
+                url: "/institutions/index",
+                where: { keyword: "", link_type: null, status: null },
+            },
+            choose: [],
+            formData: {
+                id: null,
+                name: "",
+                code: "",
+                thumb: "",
+                link_type: 1,
+                link_url: "",
+                description: "",
+                sort: 0,
+                status: 1
+            },
+            isEdit: false,
+            formVisible: false,
+            formRules: {
+                name: [{ required: true, message: "请输入院校名称", trigger: "blur" }],
+                code: [{ required: true, message: "请输入院校代码", trigger: "blur" }],
+                link_type: [{ required: true, message: "请选择链接类型", trigger: "change" }],
+                link_url: [{ required: true, message: "请输入详情地址", trigger: "blur" }],
+            },
+            permissionMap: {
+                delete: "sys:institution:delete",
+                edit: "sys:institution:edit",
+                add: "sys:institution:add",
+                index: "sys:institution:index",
+                status: "sys:institution:status",
+                sort: "sys:institution:sort",
+                options: "sys:institution:options",
+            }
+        };
+    },
+    computed: { ...mapGetters(["permission"]) },
+    methods: {
+        resetSearch() {
+            this.table.where = { keyword: "", link_type: null, status: null };
+            this.$refs.table.reload();
+        },
+        openForm(row) {
+            if (row) {
+                this.isEdit = true;
+                this.formData = {
+                    id: row.id,
+                    name: row.name,
+                    code: row.code,
+                    thumb: row.thumb || "",
+                    link_type: row.link_type,
+                    link_url: row.link_url,
+                    description: row.description || "",
+                    sort: row.sort || 0,
+                    status: row.status
+                };
+            } else {
+                this.isEdit = false;
+                this.formData = {
+                    id: null,
+                    name: "",
+                    code: "",
+                    thumb: "",
+                    link_type: 1,
+                    link_url: "",
+                    description: "",
+                    sort: 0,
+                    status: 1
+                };
+            }
+            this.formVisible = true;
+        },
+        async saveForm() {
+            this.$refs.formRef.validate(async (valid) => {
+                if (!valid) return;
+                try {
+                    const res = await this.$http.post("/institutions/edit", this.formData);
+                    if (res.data.code === 0) {
+                        this.$message.success("保存成功");
+                        this.formVisible = false;
+                        this.$refs.table.reload();
+                    } else {
+                        this.$message.error(res.data.msg);
+                    }
+                } catch (e) {
+                    this.$message.error("保存失败:" + e.message);
+                }
+            });
+        },
+        async remove(row) {
+            const ids = row ? [row.id] : this.choose.map((d) => d.id);
+            if (!ids.length) return this.$message.warning("请选择数据");
+            this.$confirm("确定要删除选中院校吗?", "提示", { type: "warning" }).then(async () => {
+                try {
+                    const res = await this.$http.post("/institutions/delete", { id: ids });
+                    if (res.data.code === 0) {
+                        this.$message.success("删除成功");
+                        this.$refs.table.reload();
+                    } else {
+                        this.$message.error(res.data.msg);
+                    }
+                } catch (e) {
+                    this.$message.error("删除失败:" + e.message);
+                }
+            });
+        },
+        batchRemove() {
+            this.remove();
+        },
+        async editStatus(row) {
+            try {
+                const res = await this.$http.post("/institutions/status", { id: row.id, status: row.status });
+                if (res.data.code !== 0) {
+                    this.$message.error(res.data.msg);
+                    // 恢复原状态
+                    row.status = row.status === 1 ? 2 : 1;
+                }
+            } catch (e) {
+                this.$message.error("状态更新失败:" + e.message);
+                // 恢复原状态
+                row.status = row.status === 1 ? 2 : 1;
+            }
+        },
+        async updateSort(row) {
+            try {
+                const res = await this.$http.post("/institutions/sort", { id: row.id, sort: row.sort });
+                if (res.data.code !== 0) {
+                    this.$message.error(res.data.msg);
+                }
+            } catch (e) {
+                this.$message.error("排序更新失败:" + e.message);
+            }
+        }
+    },
+};
+</script>
+
+<style scoped></style>

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

@@ -0,0 +1,73 @@
+<?php
+// +----------------------------------------------------------------------
+// | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
+// +----------------------------------------------------------------------
+// | 版权所有 2017~2021 LARAVEL研发中心
+// +----------------------------------------------------------------------
+// | 官方网站: http://www.laravel.cn
+// +----------------------------------------------------------------------
+// | Author: laravel开发员 <laravel.qq.com>
+// +----------------------------------------------------------------------
+
+namespace App\Http\Controllers\Admin;
+
+use App\Services\Common\InstitutionService;
+
+/**
+ * 院校管理-控制器
+ * @author laravel开发员
+ * @since 2024/01/08
+ * Class InstitutionController
+ * @package App\Http\Controllers\Admin
+ */
+class InstitutionController extends Backend
+{
+    /**
+     * 构造函数
+     * @author laravel开发员
+     * @since 2024/01/08
+     * InstitutionController constructor.
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->service = new InstitutionService();
+    }
+
+    /**
+     * 列表
+     * @return array
+     */
+    public function index()
+    {
+        $pageSize = request()->get('limit', 10);
+        $list = $this->service->getDataList(request()->all(), $pageSize);
+        $message = array(
+            "msg" => '操作成功',
+            "code" => 0,
+            "data" => isset($list['list']) ? $list['list'] : [],
+            "count" => isset($list['total']) ? $list['total'] : 0,
+        );
+        return $message;
+    }
+
+    /**
+     * 选项列表
+     * @return mixed
+     */
+    public function options()
+    {
+        $result = $this->service->options();
+        return message(1002, true, $result);
+    }
+
+    /**
+     * 设置排序
+     * @return mixed
+     */
+    public function sort()
+    {
+        $result = $this->service->sort();
+        return $result;
+    }
+}

+ 50 - 0
app/Models/InstitutionModel.php

@@ -0,0 +1,50 @@
+<?php
+// +----------------------------------------------------------------------
+// | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
+// +----------------------------------------------------------------------
+// | 版权所有 2017~2021 LARAVEL研发中心
+// +----------------------------------------------------------------------
+// | 官方网站: http://www.laravel.cn
+// +----------------------------------------------------------------------
+// | Author: laravel开发员 <laravel.qq.com>
+// +----------------------------------------------------------------------
+
+namespace App\Models;
+
+/**
+ * 院校-模型
+ * @author laravel开发员
+ * @since 2024/01/08
+ * @package App\Models
+ */
+class InstitutionModel extends BaseModel
+{
+    // 设置数据表
+    protected $table = 'institutions';
+
+    /**
+     * 格式化信息
+     * @param array $info 实体数据对象
+     * @return array 返回实体对象
+     * @author laravel开发员
+     * @date 2024/01/08
+     */
+    public function formatInfo($info)
+    {
+        // 格式化链接类型
+        if (isset($info['link_type'])) {
+            $linkTypeMap = [
+                1 => 'Web链接',
+                2 => '小程序链接'
+            ];
+            $info['link_type_text'] = isset($linkTypeMap[$info['link_type']]) ? $linkTypeMap[$info['link_type']] : '未知类型';
+        }
+
+        // 格式化状态
+        if (isset($info['status'])) {
+            $info['status_text'] = $info['status'] == 1 ? '正常' : '不显示';
+        }
+
+        return $info;
+    }
+}

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

@@ -0,0 +1,227 @@
+<?php
+
+namespace App\Services\Common;
+
+use App\Models\InstitutionModel;
+use App\Models\ActionLogModel;
+use App\Services\BaseService;
+
+/**
+ * InstitutionService 服务类
+ * @author laravel开发员
+ * @since 2024/01/08
+ * Class InstitutionService
+ * @package App\Services\Common
+ */
+class InstitutionService extends BaseService
+{
+    public static $instance = null;
+
+    /**
+     * 构造函数
+     * @since 2024/01/08
+     */
+    public function __construct()
+    {
+        $this->model = new InstitutionModel();
+    }
+
+    /**
+     * 静态入口
+     * @return static|null
+     */
+    public static function make()
+    {
+        if (!self::$instance) {
+            self::$instance = (new static());
+        }
+        return self::$instance;
+    }
+
+    /**
+     * 获取院校列表
+     * @param $params
+     * @param int $pageSize
+     * @return array
+     */
+    public function getDataList($params, $pageSize = 15)
+    {
+        $query = $this->getQuery($params);
+
+        $model = clone $query;
+        $counts = [
+            'count' => $model->count('id'),
+        ];
+
+        $list = $query->select(['*'])
+            ->orderBy('sort', 'desc')
+            ->orderBy('id', 'desc')
+            ->paginate($pageSize > 0 ? $pageSize : 9999999);
+        $list = $list ? $list->toArray() : [];
+
+        if ($list) {
+            foreach ($list['data'] as &$item) {
+                // 格式化时间
+                $item['create_time'] = $item['create_time'] ? datetime($item['create_time'], 'Y-m-d H:i:s') : '';
+                $item['update_time'] = $item['update_time'] ? datetime($item['update_time'], 'Y-m-d H:i:s') : '';
+                // 格式化状态
+                $item['status_text'] = $item['status'] == 1 ? '正常' : '不显示';
+                // 格式化链接类型
+                $item['link_type_text'] = $item['link_type'] == 1 ? 'Web链接' : '小程序链接';
+            }
+        }
+
+        return [
+            'pageSize' => $pageSize,
+            'total' => isset($list['total']) ? $list['total'] : 0,
+            'counts' => $counts,
+            'list' => isset($list['data']) ? $list['data'] : []
+        ];
+    }
+
+    /**
+     * 查询院校
+     * @param $params
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getQuery($params)
+    {
+        $where = ['mark' => 1];
+        $status = isset($params['status']) ? $params['status'] : 0;
+        $linkType = isset($params['link_type']) ? $params['link_type'] : 0;
+
+        if ($status > 0) {
+            $where['status'] = $status;
+        }
+        if ($linkType > 0) {
+            $where['link_type'] = $linkType;
+        }
+
+        return $this->model->where($where)
+            ->where(function ($query) use ($params) {
+                $keyword = isset($params['keyword']) ? $params['keyword'] : '';
+                if ($keyword) {
+                    $query->where(function ($q) use ($keyword) {
+                        $q->where('name', 'like', "%{$keyword}%")
+                            ->orWhere('code', 'like', "%{$keyword}%");
+                    });
+                }
+
+                // 处理日期区间
+                $date = isset($params['date']) ? $params['date'] : [];
+                $start = isset($date[0]) ? $date[0] : '';
+                $end = isset($date[1]) ? $date[1] : '';
+                if ($start) {
+                    $query->where('create_time', '>=', strtotime($start));
+                }
+                if ($end) {
+                    $query->where('create_time', '<=', strtotime($end));
+                }
+            });
+    }
+
+    /**
+     * 添加或编辑院校
+     * @return array
+     */
+    public function edit()
+    {
+        $data = request()->all();
+
+        // 验证必填字段
+        if (empty($data['name'])) {
+            return message('院校名称不能为空', false);
+        }
+        if (empty($data['code'])) {
+            return message('院校代码不能为空', false);
+        }
+
+        // 检查院校代码是否重复
+        $existId = $this->checkExists('code', $data['code'], 'id', 0);
+        if ($existId && $existId != ($data['id'] ?? 0)) {
+            return message('院校代码已存在', false);
+        }
+
+        // 确保所有必填字段都存在
+        $data = [
+            'name' => $data['name'] ?? '',
+            'code' => $data['code'] ?? '',
+            'thumb' => $data['thumb'] ?? '',
+            'link_url' => $data['link_url'] ?? '',
+            'link_type' => $data['link_type'] ?? 1,
+            'description' => $data['description'] ?? '',
+            'sort' => $data['sort'] ?? 0,
+            'status' => $data['status'] ?? 1,
+        ];
+
+        return parent::edit($data);
+    }
+
+    /**
+     * 删除院校
+     * @return array
+     */
+    public function delete()
+    {
+        // 设置日志标题
+        ActionLogModel::setRecord(session('userId'), ['type' => 1, 'title' => "删除院校信息", 'content' => json_encode(request()->post(), 256), 'module' => 'admin']);
+        ActionLogModel::record();
+
+        // 删除七天之前标记软删除的数据
+        $this->model->where('mark', 0)->where('update_time', '<=', time() - 7 * 86400)->delete();
+        return parent::delete();
+    }
+
+    /**
+     * 院校选项
+     * @return array
+     */
+    public function options()
+    {
+        // 获取参数
+        $param = request()->all();
+        // 关键词
+        $keyword = getter($param, "keyword");
+        $datas = $this->model
+            ->where(function ($query) {
+                $query->where(['status' => 1, 'mark' => 1]);
+            })
+            ->where(function ($query) use ($keyword) {
+                if ($keyword) {
+                    $query->where('name', 'like', "%{$keyword}%");
+                }
+            })
+            ->select(['id', 'name', 'code'])
+            ->orderBy('sort', 'desc')
+            ->orderBy('id', 'desc')
+            ->get();
+
+        return $datas ? $datas->toArray() : [];
+    }
+
+    /**
+     * 设置排序
+     * @return array
+     */
+    public function sort()
+    {
+        $data = request()->all();
+        if (!$data['id']) {
+            return message('记录ID不能为空', false);
+        }
+        if (!isset($data['sort'])) {
+            return message('排序值不能为空', false);
+        }
+
+        $error = '';
+        $item = [
+            'id' => $data['id'],
+            'sort' => $data['sort']
+        ];
+        $rowId = $this->model->edit($item, $error);
+        if (!$rowId) {
+            return message($error, false);
+        }
+        return message();
+    }
+}

+ 11 - 1
routes/web.php

@@ -33,6 +33,7 @@ use App\Http\Controllers\Admin\VideoCategoryController;
 use App\Http\Controllers\Admin\VideoCoursesController;
 use App\Http\Controllers\Admin\ComplaintController;
 use App\Http\Controllers\Admin\VipController;
+use App\Http\Controllers\Admin\InstitutionController;
 
 /*
 |--------------------------------------------------------------------------
@@ -275,4 +276,13 @@ Route::post('/vips/delete', [VipController::class, 'delete']);
 Route::post('/vips/status', [VipController::class, 'status']);
 Route::post('/vips/sort', [VipController::class, 'sort']);
 Route::post('/vips/options', [VipController::class, 'options']);
-Route::post('/vips/batchSet', [VipController::class, 'batchSet']);
+Route::post('/vips/batchSet', [VipController::class, 'batchSet']);
+
+// 院校管理
+Route::get('/institutions/index', [InstitutionController::class, 'index']);
+Route::get('/institutions/info', [InstitutionController::class, 'info']);
+Route::post('/institutions/edit', [InstitutionController::class, 'edit']);
+Route::post('/institutions/delete', [InstitutionController::class, 'delete']);
+Route::post('/institutions/status', [InstitutionController::class, 'status']);
+Route::post('/institutions/sort', [InstitutionController::class, 'sort']);
+Route::get('/institutions/options', [InstitutionController::class, 'options']);