PHP 岗位管理

wen PHP项目 3

本文目录导读:

PHP 岗位管理

  1. 系统功能架构
  2. 数据库设计
  3. 核心代码实现
  4. 权限控制实现
  5. 最佳实践与优化

我来为你全面讲解 PHP 岗位管理系统的开发,从需求分析到具体实现。

系统功能架构

核心功能模块

// 岗位管理系统功能结构
1. 岗位基础管理
   - 岗位创建、编辑、删除
   - 岗位编码生成
   - 岗位状态管理
2. 岗位分类管理
   - 部门分类
   - 职级分类
   - 岗位类型
3. 权限管理
   - 岗位权限分配
   - 数据权限控制
4. 人员关联
   - 人员-岗位绑定
   - 岗位编制管理

数据库设计

数据表结构

-- 岗位信息表
CREATE TABLE `positions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `position_code` varchar(20) NOT NULL COMMENT '岗位编码',
  `position_name` varchar(50) NOT NULL COMMENT '岗位名称',
  `department_id` int(11) NOT NULL COMMENT '所属部门',
  `position_type` tinyint(4) DEFAULT '1' COMMENT '岗位类型:1管理岗 2技术岗 3职能岗',
  `position_level` varchar(20) DEFAULT NULL COMMENT '岗位职级',
  `head_count` int(11) DEFAULT '1' COMMENT '编制人数',
  `current_count` int(11) DEFAULT '0' COMMENT '在职人数',
  `description` text COMMENT '岗位描述',
  `requirements` text COMMENT '任职要求',
  `salary_min` decimal(10,2) DEFAULT NULL COMMENT '薪资下限',
  `salary_max` decimal(10,2) DEFAULT NULL COMMENT '薪资上限',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态:1启用 0禁用',
  `created_by` int(11) DEFAULT NULL COMMENT '创建人',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_position_code` (`position_code`),
  KEY `idx_department` (`department_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='岗位信息表';
-- 岗位-用户关联表
CREATE TABLE `position_users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `position_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `is_primary` tinyint(1) DEFAULT '1' COMMENT '是否主岗位',
  `start_date` date DEFAULT NULL,
  `end_date` date DEFAULT NULL,
  `status` tinyint(4) DEFAULT '1',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_position_user` (`position_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='岗位用户关联表';

核心代码实现

岗位管理控制器

<?php
namespace App\Controllers;
use App\Models\PositionModel;
use App\Helpers\Validator;
use App\Helpers\Response;
class PositionController extends BaseController
{
    protected $positionModel;
    public function __construct()
    {
        parent::__construct();
        $this->positionModel = new PositionModel();
    }
    /**
     * 岗位列表
     */
    public function index()
    {
        $page = $_GET['page'] ?? 1;
        $limit = $_GET['limit'] ?? 20;
        $keyword = $_GET['keyword'] ?? '';
        $departmentId = $_GET['department_id'] ?? '';
        $status = $_GET['status'] ?? '';
        $where = [];
        if (!empty($keyword)) {
            $where['position_name LIKE'] = "%{$keyword}%";
        }
        if (!empty($departmentId)) {
            $where['department_id'] = $departmentId;
        }
        if ($status !== '') {
            $where['status'] = $status;
        }
        $result = $this->positionModel->getList($where, $page, $limit);
        return Response::success($result);
    }
    /**
     * 创建岗位
     */
    public function create()
    {
        // 权限检查
        $this->checkPermission('position:create');
        // 获取并验证数据
        $data = [
            'position_code' => $_POST['position_code'] ?? '',
            'position_name' => $_POST['position_name'] ?? '',
            'department_id' => $_POST['department_id'] ?? 0,
            'position_type' => $_POST['position_type'] ?? 1,
            'position_level' => $_POST['position_level'] ?? '',
            'head_count' => $_POST['head_count'] ?? 1,
            'description' => $_POST['description'] ?? '',
            'requirements' => $_POST['requirements'] ?? '',
            'salary_min' => $_POST['salary_min'] ?? 0,
            'salary_max' => $_POST['salary_max'] ?? 0,
        ];
        // 验证规则
        $rules = [
            'position_code' => 'required|max:20|unique:positions',
            'position_name' => 'required|max:50',
            'department_id' => 'required|numeric',
            'head_count' => 'numeric|min:1',
            'salary_min' => 'numeric|min:0',
            'salary_max' => 'numeric|min:0'
        ];
        $validator = new Validator();
        if (!$validator->validate($data, $rules)) {
            return Response::error($validator->getErrors(), 400);
        }
        $data['created_by'] = $_SESSION['user_id'];
        $result = $this->positionModel->create($data);
        if ($result) {
            // 记录操作日志
            $this->logOperation('create_position', $data['position_code']);
            return Response::success(['id' => $result], '创建成功');
        }
        return Response::error('创建失败', 500);
    }
    /**
     * 更新岗位
     */
    public function update($id)
    {
        $this->checkPermission('position:update');
        $position = $this->positionModel->find($id);
        if (!$position) {
            return Response::error('岗位不存在', 404);
        }
        $data = [
            'position_name' => $_POST['position_name'] ?? $position['position_name'],
            'position_type' => $_POST['position_type'] ?? $position['position_type'],
            'position_level' => $_POST['position_level'] ?? $position['position_level'],
            'head_count' => $_POST['head_count'] ?? $position['head_count'],
            'description' => $_POST['description'] ?? $position['description'],
            'requirements' => $_POST['requirements'] ?? $position['requirements'],
            'salary_min' => $_POST['salary_min'] ?? $position['salary_min'],
            'salary_max' => $_POST['salary_max'] ?? $position['salary_max'],
        ];
        $result = $this->positionModel->update($id, $data);
        if ($result) {
            $this->logOperation('update_position', $position['position_code']);
            return Response::success($data, '更新成功');
        }
        return Response::error('更新失败', 500);
    }
    /**
     * 删除岗位
     */
    public function delete($id)
    {
        $this->checkPermission('position:delete');
        // 检查是否有关联人员
        $userCount = $this->positionModel->getUsersCount($id);
        if ($userCount > 0) {
            return Response::error('该岗位已关联人员,无法删除', 400);
        }
        $result = $this->positionModel->delete($id);
        if ($result) {
            return Response::success(null, '删除成功');
        }
        return Response::error('删除失败', 500);
    }
    /**
     * 岗位分配人员
     */
    public function assignUsers($positionId)
    {
        $this->checkPermission('position:assign');
        $userId = $_POST['user_id'] ?? 0;
        $action = $_POST['action'] ?? 'assign'; // assign/remove
        if ($action === 'assign') {
            $result = $this->positionModel->assignUser($positionId, $userId);
        } else {
            $result = $this->positionModel->removeUser($positionId, $userId);
        }
        if ($result) {
            return Response::success(null, '操作成功');
        }
        return Response::error('操作失败', 500);
    }
    /**
     * 导出岗位列表
     */
    public function export()
    {
        $this->checkPermission('position:export');
        $positions = $this->positionModel->getAllForExport();
        // 生成Excel
        $fileName = '岗位列表_' . date('YmdHis') . '.xls';
        header('Content-Type: application/vnd.ms-excel');
        header("Content-Disposition: attachment; filename={$fileName}");
        echo $this->generateExcelContent($positions);
    }
}

岗位模型

<?php
namespace App\Models;
use App\Database\Database;
use PDO;
class PositionModel extends BaseModel
{
    protected $table = 'positions';
    /**
     * 获取岗位列表(带部门信息)
     */
    public function getList($where = [], $page = 1, $limit = 20)
    {
        $offset = ($page - 1) * $limit;
        $sql = "SELECT p.*, d.department_name,
                (p.head_count - p.current_count) as available_count
                FROM positions p
                LEFT JOIN departments d ON p.department_id = d.id";
        $conditions = [];
        $params = [];
        foreach ($where as $field => $value) {
            if (strpos($field, ' LIKE') !== false) {
                $conditions[] = "p." . str_replace(' LIKE', '', $field) . " LIKE ?";
                $params[] = $value;
            } else {
                $conditions[] = "p.{$field} = ?";
                $params[] = $value;
            }
        }
        if (!empty($conditions)) {
            $sql .= " WHERE " . implode(" AND ", $conditions);
        }
        $sql .= " ORDER BY p.created_at DESC LIMIT ? OFFSET ?";
        $params[] = $limit;
        $params[] = $offset;
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        $list = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // 获取总数
        $countSql = "SELECT COUNT(*) FROM positions p";
        if (!empty($conditions)) {
            $countSql .= " WHERE " . implode(" AND ", $conditions);
        }
        $stmt = $this->db->prepare($countSql);
        $stmt->execute(array_slice($params, 0, -2));
        $total = $stmt->fetchColumn();
        return [
            'list' => $list,
            'total' => $total,
            'page' => $page,
            'limit' => $limit
        ];
    }
    /**
     * 分配用户到岗位
     */
    public function assignUser($positionId, $userId)
    {
        try {
            $this->db->beginTransaction();
            // 检查编制
            $position = $this->find($positionId);
            if (!$position) {
                throw new \Exception('岗位不存在');
            }
            if ($position['current_count'] >= $position['head_count']) {
                throw new \Exception('岗位编制已满');
            }
            // 插入关联
            $sql = "INSERT INTO position_users (position_id, user_id) 
                    VALUES (?, ?)";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$positionId, $userId]);
            // 更新在职人数
            $sql = "UPDATE positions SET current_count = current_count + 1 
                    WHERE id = ?";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$positionId]);
            $this->db->commit();
            return true;
        } catch (\Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }
    /**
     * 检查岗位编码唯一性
     */
    public function checkCodeUnique($code, $excludeId = null)
    {
        $sql = "SELECT id FROM positions WHERE position_code = ?";
        $params = [$code];
        if ($excludeId) {
            $sql .= " AND id != ?";
            $params[] = $excludeId;
        }
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchColumn() ? false : true;
    }
    /**
     * 统计部门下岗位数
     */
    public function countByDepartment($departmentId)
    {
        $sql = "SELECT COUNT(*) FROM positions WHERE department_id = ?";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$departmentId]);
        return $stmt->fetchColumn();
    }
}

前端页面示例

<!-- 岗位列表页面 -->
<!DOCTYPE html>
<html>
<head>岗位管理</title>
    <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
</head>
<body>
    <div id="app">
        <el-container>
            <el-header>
                <h2>岗位管理</h2>
            </el-header>
            <el-main>
                <!-- 搜索区域 -->
                <el-form :inline="true" class="search-form">
                    <el-form-item label="岗位名称">
                        <el-input v-model="searchForm.keyword" placeholder="请输入岗位名称"></el-input>
                    </el-form-item>
                    <el-form-item label="部门">
                        <el-select v-model="searchForm.department" placeholder="请选择部门">
                            <el-option 
                                v-for="item in departments" 
                                :key="item.id" 
                                :label="item.name" 
                                :value="item.id">
                            </el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item label="状态">
                        <el-select v-model="searchForm.status">
                            <el-option label="全部" value=""></el-option>
                            <el-option label="启用" value="1"></el-option>
                            <el-option label="禁用" value="0"></el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item>
                        <el-button type="primary" @click="search">搜索</el-button>
                        <el-button @click="resetSearch">重置</el-button>
                    </el-form-item>
                </el-form>
                <!-- 操作按钮 -->
                <div class="table-toolbar">
                    <el-button type="primary" @click="openCreateDialog">
                        <el-icon><Plus /></el-icon>新增岗位
                    </el-button>
                    <el-button @click="exportExcel">导出</el-button>
                </div>
                <!-- 表格 -->
                <el-table :data="positionList" border stripe>
                    <el-table-column prop="position_code" label="岗位编码" width="120"></el-table-column>
                    <el-table-column prop="position_name" label="岗位名称" width="150"></el-table-column>
                    <el-table-column prop="department_name" label="所属部门"></el-table-column>
                    <el-table-column prop="position_type" label="岗位类型">
                        <template #default="{ row }">
                            <el-tag :type="getTypeTag(row.position_type)">
                                {{ getTypeName(row.position_type) }}
                            </el-tag>
                        </template>
                    </el-table-column>
                    <el-table-column prop="head_count" label="编制人数" width="100"></el-table-column>
                    <el-table-column prop="current_count" label="在职人数" width="100"></el-table-column>
                    <el-table-column prop="position_level" label="职级" width="80"></el-table-column>
                    <el-table-column prop="status" label="状态">
                        <template #default="{ row }">
                            <el-switch 
                                v-model="row.status" 
                                :active-value="1" 
                                :inactive-value="0"
                                @change="changeStatus(row)">
                            </el-switch>
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" width="200" fixed="right">
                        <template #default="{ row }">
                            <el-button link type="primary" @click="editPosition(row)">编辑</el-button>
                            <el-button link type="warning" @click="assignPerson(row)">人员分配</el-button>
                            <el-popconfirm 
                                title="确定删除该岗位吗?"
                                @confirm="deletePosition(row.id)"
                            >
                                <template #reference>
                                    <el-button link type="danger">删除</el-button>
                                </template>
                            </el-popconfirm>
                        </template>
                    </el-table-column>
                </el-table>
                <!-- 分页 -->
                <el-pagination 
                    v-model:current-page="page"
                    v-model:page-size="limit"
                    :total="total"
                    :page-sizes="[10, 20, 50, 100]"
                    @current-change="getList"
                    @size-change="getList">
                </el-pagination>
            </el-main>
        </el-container>
        <!-- 新增/编辑对话框 -->
        <el-dialog 
            v-model="dialogVisible" 
            :title="dialogType === 'create' ? '新增岗位' : '编辑岗位'"
            width="600px">
            <el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
                <el-form-item label="岗位编码" prop="position_code">
                    <el-input v-model="formData.position_code" :disabled="dialogType === 'edit'"></el-input>
                </el-form-item>
                <el-form-item label="岗位名称" prop="position_name">
                    <el-input v-model="formData.position_name"></el-input>
                </el-form-item>
                <el-form-item label="所属部门" prop="department_id">
                    <el-select v-model="formData.department_id" placeholder="请选择部门">
                        <el-option 
                            v-for="item in departments" 
                            :key="item.id" 
                            :label="item.name" 
                            :value="item.id">
                        </el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="岗位类型" prop="position_type">
                    <el-radio-group v-model="formData.position_type">
                        <el-radio :value="1">管理岗</el-radio>
                        <el-radio :value="2">技术岗</el-radio>
                        <el-radio :value="3">职能岗</el-radio>
                    </el-radio-group>
                </el-form-item>
                <el-form-item label="岗位职级">
                    <el-select v-model="formData.position_level">
                        <el-option label="初级" value="初级"></el-option>
                        <el-option label="中级" value="中级"></el-option>
                        <el-option label="高级" value="高级"></el-option>
                        <el-option label="资深" value="资深"></el-option>
                        <el-option label="专家" value="专家"></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item label="编制人数" prop="head_count">
                    <el-input-number v-model="formData.head_count" :min="1" :max="100"></el-input-number>
                </el-form-item>
                <el-form-item label="薪资范围">
                    <div class="salary-range">
                        <el-input-number 
                            v-model="formData.salary_min" 
                            :min="0" 
                            :max="formData.salary_max">
                        </el-input-number>
                        <span class="divider">-</span>
                        <el-input-number 
                            v-model="formData.salary_max" 
                            :min="formData.salary_min">
                        </el-input-number>
                        <span class="unit">元/月</span>
                    </div>
                </el-form-item>
                <el-form-item label="岗位描述">
                    <el-input 
                        v-model="formData.description" 
                        type="textarea" 
                        :rows="3">
                    </el-input>
                </el-form-item>
                <el-form-item label="任职要求">
                    <el-input 
                        v-model="formData.requirements" 
                        type="textarea" 
                        :rows="3">
                    </el-input>
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="dialogVisible = false">取消</el-button>
                <el-button type="primary" @click="submitForm">确定</el-button>
            </template>
        </el-dialog>
        <!-- 人员分配对话框 -->
        <el-dialog v-model="assignVisible" title="人员分配" width="500px">
            <div v-if="currentPosition">
                <h4>{{ currentPosition.position_name }}</h4>
                <p>编制人数:{{ currentPosition.head_count }},当前人数:{{ currentPosition.current_count }}</p>
                <el-divider></el-divider>
                <h5>已分配人员</h5>
                <el-table :data="assignedUsers">
                    <el-table-column prop="username" label="用户名"></el-table-column>
                    <el-table-column prop="name" label="姓名"></el-table-column>
                    <el-table-column label="操作" width="80">
                        <template #default="{ row }">
                            <el-button 
                                link 
                                type="danger" 
                                @click="removeUser(row.user_id)">
                                移除
                            </el-button>
                        </template>
                    </el-table-column>
                </el-table>
                <el-divider></el-divider>
                <h5>添加人员</h5>
                <div class="user-search">
                    <el-select 
                        v-model="selectedUser"
                        filterable
                        remote
                        :remote-method="searchUsers"
                        placeholder="搜索用户">
                        <el-option 
                            v-for="item in availableUsers" 
                            :key="item.id" 
                            :label="`${item.name} (${item.username})`" 
                            :value="item.id">
                        </el-option>
                    </el-select>
                    <el-button type="primary" @click="assignUser">添加</el-button>
                </div>
            </div>
        </el-dialog>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script src="https://unpkg.com/element-plus"></script>
    <script>
    // Vue 应用
    const { createApp, ref, onMounted, reactive } = Vue;
    const app = createApp({
        setup() {
            // 数据定义
            const positionList = ref([]);
            const departments = ref([]);
            const total = ref(0);
            const page = ref(1);
            const limit = ref(20);
            const searchForm = reactive({
                keyword: '',
                department: '',
                status: ''
            });
            const dialogVisible = ref(false);
            const dialogType = ref('create');
            const formRef = ref(null);
            const formData = reactive({
                position_code: '',
                position_name: '',
                department_id: null,
                position_type: 1,
                position_level: '',
                head_count: 1,
                description: '',
                requirements: '',
                salary_min: 0,
                salary_max: 0
            });
            const formRules = {
                position_code: [
                    { required: true, message: '请输入岗位编码', trigger: 'blur' },
                    { min: 2, max: 20, message: '长度在2-20个字符', trigger: 'blur' }
                ],
                position_name: [
                    { required: true, message: '请输入岗位名称', trigger: 'blur' }
                ],
                department_id: [
                    { required: true, message: '请选择部门', trigger: 'change' }
                ]
            };
            // 人员分配相关
            const assignVisible = ref(false);
            const currentPosition = ref(null);
            const assignedUsers = ref([]);
            const availableUsers = ref([]);
            const selectedUser = ref(null);
            // 方法定义
            const getList = async () => {
                const params = new URLSearchParams({
                    page: page.value,
                    limit: limit.value,
                    keyword: searchForm.keyword,
                    department_id: searchForm.department,
                    status: searchForm.status
                });
                try {
                    const response = await fetch(`/api/positions?${params}`);
                    const data = await response.json();
                    positionList.value = data.data.list;
                    total.value = data.data.total;
                } catch (error) {
                    console.error('获取岗位列表失败:', error);
                }
            };
            const getDepartments = async () => {
                try {
                    const response = await fetch('/api/departments');
                    const data = await response.json();
                    departments.value = data.data;
                } catch (error) {
                    console.error('获取部门列表失败:', error);
                }
            };
            const openCreateDialog = () => {
                dialogType.value = 'create';
                Object.assign(formData, {
                    position_code: '',
                    position_name: '',
                    department_id: null,
                    position_type: 1,
                    position_level: '',
                    head_count: 1,
                    description: '',
                    requirements: '',
                    salary_min: 0,
                    salary_max: 0
                });
                dialogVisible.value = true;
            };
            const editPosition = (row) => {
                dialogType.value = 'edit';
                Object.assign(formData, row);
                dialogVisible.value = true;
            };
            const submitForm = async () => {
                try {
                    await formRef.value.validate();
                    const url = dialogType.value === 'create' 
                        ? '/api/positions' 
                        : `/api/positions/${formData.id}`;
                    const method = dialogType.value === 'create' ? 'POST' : 'PUT';
                    const response = await fetch(url, {
                        method: method,
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify(formData)
                    });
                    const data = await response.json();
                    if (data.code === 0) {
                        dialogVisible.value = false;
                        ElMessage.success(data.message);
                        getList();
                    } else {
                        ElMessage.error(data.message);
                    }
                } catch (error) {
                    console.error('提交失败:', error);
                }
            };
            const deletePosition = async (id) => {
                try {
                    const response = await fetch(`/api/positions/${id}`, {
                        method: 'DELETE'
                    });
                    const data = await response.json();
                    if (data.code === 0) {
                        ElMessage.success('删除成功');
                        getList();
                    } else {
                        ElMessage.error(data.message);
                    }
                } catch (error) {
                    console.error('删除失败:', error);
                }
            };
            const assignPerson = (row) => {
                currentPosition.value = row;
                assignVisible.value = true;
                getAssignedUsers(row.id);
            };
            const getAssignedUsers = async (positionId) => {
                try {
                    const response = await fetch(`/api/positions/${positionId}/users`);
                    const data = await response.json();
                    assignedUsers.value = data.data;
                } catch (error) {
                    console.error('获取已分配人员失败:', error);
                }
            };
            const searchUsers = async (query) => {
                try {
                    const response = await fetch(`/api/users/search?keyword=${query}`);
                    const data = await response.json();
                    availableUsers.value = data.data;
                } catch (error) {
                    console.error('搜索用户失败:', error);
                }
            };
            const assignUser = async () => {
                if (!selectedUser.value) {
                    ElMessage.warning('请选择用户');
                    return;
                }
                try {
                    const response = await fetch(`/api/positions/${currentPosition.value.id}/assign`, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({
                            user_id: selectedUser.value,
                            action: 'assign'
                        })
                    });
                    const data = await response.json();
                    if (data.code === 0) {
                        ElMessage.success('添加成功');
                        getAssignedUsers(currentPosition.value.id);
                        getList();
                        selectedUser.value = null;
                    } else {
                        ElMessage.error(data.message);
                    }
                } catch (error) {
                    console.error('添加人员失败:', error);
                }
            };
            const removeUser = async (userId) => {
                try {
                    const response = await fetch(`/api/positions/${currentPosition.value.id}/assign`, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({
                            user_id: userId,
                            action: 'remove'
                        })
                    });
                    const data = await response.json();
                    if (data.code === 0) {
                        ElMessage.success('移除成功');
                        getAssignedUsers(currentPosition.value.id);
                        getList();
                    } else {
                        ElMessage.error(data.message);
                    }
                } catch (error) {
                    console.error('移除人员失败:', error);
                }
            };
            const search = () => {
                page.value = 1;
                getList();
            };
            const resetSearch = () => {
                searchForm.keyword = '';
                searchForm.department = '';
                searchForm.status = '';
                getList();
            };
            const exportExcel = () => {
                window.location.href = '/api/positions/export';
            };
            const getTypeName = (type) => {
                const types = { 1: '管理岗', 2: '技术岗', 3: '职能岗' };
                return types[type] || '未知';
            };
            const getTypeTag = (type) => {
                const tags = { 1: 'danger', 2: 'success', 3: 'warning' };
                return tags[type] || 'info';
            };
            // 生命周期
            onMounted(() => {
                getList();
                getDepartments();
            });
            return {
                positionList,
                departments,
                total,
                page,
                limit,
                searchForm,
                dialogVisible,
                dialogType,
                formRef,
                formData,
                formRules,
                assignVisible,
                currentPosition,
                assignedUsers,
                availableUsers,
                selectedUser,
                getList,
                openCreateDialog,
                editPosition,
                submitForm,
                deletePosition,
                assignPerson,
                searchUsers,
                assignUser,
                removeUser,
                search,
                resetSearch,
                exportExcel,
                getTypeName,
                getTypeTag
            };
        }
    });
    app.use(ElementPlus);
    app.mount('#app');
    </script>
    <style>
    .search-form {
        margin-bottom: 20px;
        padding: 20px;
        background: #f5f7fa;
        border-radius: 4px;
    }
    .table-toolbar {
        margin-bottom: 20px;
        display: flex;
        justify-content: space-between;
    }
    .salary-range {
        display: flex;
        align-items: center;
        gap: 10px;
    }
    .divider {
        color: #999;
    }
    .unit {
        color: #666;
        font-size: 14px;
    }
    .user-search {
        display: flex;
        gap: 10px;
    }
    .user-search .el-select {
        flex: 1;
    }
    </style>
</body>
</html>

权限控制实现

<?php
namespace App\Helpers;
class AccessControl
{
    /**
     * 检查用户权限
     */
    public static function checkPermission($permission)
    {
        if (!isset($_SESSION['user_id'])) {
            header('HTTP/1.1 401 Unauthorized');
            echo json_encode(['error' => '未登录']);
            exit;
        }
        // 获取用户角色权限
        $sql = "SELECT p.permission_code 
                FROM role_permissions rp
                INNER JOIN permissions p ON rp.permission_id = p.id
                INNER JOIN user_roles ur ON ur.role_id = rp.role_id
                WHERE ur.user_id = ?";
        $db = Database::getInstance();
        $stmt = $db->prepare($sql);
        $stmt->execute([$_SESSION['user_id']]);
        $permissions = $stmt->fetchAll(PDO::FETCH_COLUMN);
        if (!in_array($permission, $permissions)) {
            header('HTTP/1.1 403 Forbidden');
            echo json_encode(['error' => '没有权限']);
            exit;
        }
    }
    /**
     * 数据权限过滤
     */
    public static function dataScope($departmentId = '')
    {
        $userDept = $_SESSION['department_id'] ?? '';
        $userLevel = $_SESSION['user_level'] ?? '';
        // 超级管理员可查看所有数据
        if ($userLevel === 'super') {
            return '';
        }
        // 部门经理可查看本部门
        if ($userLevel === 'manager') {
            return "department_id = {$userDept}";
        }
        // 普通用户仅查看自己
        if ($userLevel === 'user') {
            return "created_by = {$_SESSION['user_id']}";
        }
        return '';
    }
}

最佳实践与优化

安全性优化

// SQL注入防护
// 使用预处理语句
$stmt = $db->prepare("SELECT * FROM positions WHERE id = ?");
$stmt->execute([$id]);
// XSS防护
function sanitizeOutput($data) {
    return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
}
// CSRF防护
function generateCSRFToken() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

性能优化

// 缓存热门岗位数据
class PositionCache {
    public static function getHotPositions() {
        $cacheKey = 'hot_positions';
        $data = Redis::get($cacheKey);
        if (!$data) {
            $db = Database::getInstance();
            $stmt = $db->query("SELECT * FROM positions WHERE status=1 ORDER BY view_count DESC LIMIT 10");
            $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
            Redis::set($cacheKey, $data, 3600);
        }
        return $data;
    }
}

日志记录

class OperationLog {
    public static function record($action, $positionId) {
        $logData = [
            'user_id' => $_SESSION['user_id'],
            'action' => $action,
            'position_id' => $positionId,
            'ip' => $_SERVER['REMOTE_ADDR'],
            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
            'timestamp' => date('Y-m-d H:i:s')
        ];
        // 写入日志
        $db = Database::getInstance();
        $sql = "INSERT INTO operation_logs (user_id, action, position_id, ip, user_agent, created_at) 
                VALUES (?, ?, ?, ?, ?, ?)";
        $stmt = $db->prepare($sql);
        $stmt->execute([
            $logData['user_id'],
            $logData['action'],
            $logData['position_id'],
            $logData['ip'],
            $logData['user_agent'],
            $logData['timestamp']
        ]);
    }
}

这个岗位管理系统包含了完整的CRUD操作、人员分配、权限控制等核心功能,你可以根据实际需求扩展功能,如批量导入导出、岗位对比分析、审批流程等,如果需要特定功能的详细实现,请告诉我。

抱歉,评论功能暂时关闭!