律师执业证档案管理软件:从零搭建全栈解决方案
一、环境准备与项目初始化
本方案采用前后端分离架构,前端使用Vue 3 + Element Plus,后端使用Node.js + Express,数据库使用SQLite(便于单机部署)。
1.1 开发环境安装
首先安装Node.js(版本18.0.0或更高)。访问Node.js官网(https://nodejs.org/)下载LTS版本并安装。安装完成后,在终端执行以下命令验证:
```bash node --version npm --version ```
1.2 创建项目目录结构
在本地创建一个工作目录,并初始化前后端项目。
```bash 创建项目根目录 mkdir lawyer-archive-system cd lawyer-archive-system 创建后端项目 mkdir backend cd backend npm init -y npm install express sqlite3 cors multer dotenv npm install --save-dev nodemon 创建前端项目(返回根目录) cd .. npm create vue@latest frontend 创建过程中,根据提示选择以下配置: ✔ Add TypeScript? No ✔ Add JSX Support? No ✔ Add Vue Router for Single Page Application development? Yes ✔ Add Pinia for state management? Yes ✔ Add Vitest for Unit Testing? No ✔ Add Cypress for both Unit and End-to-End testing? No ✔ Add ESLint for code quality? Yes ✔ Add Prettier for code formatting? Yes cd frontend npm install npm install element-plus axios ```
二、后端API开发
2.1 数据库初始化
在backend目录下创建database.js文件,用于初始化数据库和表结构。
```javascript // backend/database.js const sqlite3 = require('sqlite3').verbose(); const path = require('path'); // 数据库文件路径 const dbPath = path.join(__dirname, 'lawyer_archive.db'); const db = new sqlite3.Database(dbPath, (err) => { if (err) { console.error('无法连接数据库:', err.message); } else { console.log('已连接到SQLite数据库。'); } }); // 创建律师档案表 db.serialize(() => { db.run(`CREATE TABLE IF NOT EXISTS lawyer_archives ( id INTEGER PRIMARY KEY AUTOINCREMENT, license_number TEXT UNIQUE NOT NULL, name TEXT NOT NULL, gender TEXT, birth_date TEXT, id_card TEXT, phone TEXT, email TEXT, law_firm TEXT, practice_city TEXT, first_issue_date TEXT, valid_until TEXT, annual_check_status TEXT DEFAULT '未年检', archive_status TEXT DEFAULT '正常', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP )`); // 创建证件扫描件关联表 db.run(`CREATE TABLE IF NOT EXISTS archive_attachments ( id INTEGER PRIMARY KEY AUTOINCREMENT, archive_id INTEGER NOT NULL, file_name TEXT NOT NULL, file_path TEXT NOT NULL, file_type TEXT, upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (archive_id) REFERENCES lawyer_archives(id) ON DELETE CASCADE )`); }); module.exports = db; ```
2.2 核心API接口开发
创建backend/app.js作为主应用文件。
```javascript // backend/app.js const express = require('express'); const cors = require('cors'); const multer = require('multer'); const path = require('path'); const fs = require('fs'); const db = require('./database'); const app = express(); const PORT = 3000; // 确保上传目录存在 const uploadDir = path.join(__dirname, 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }); } // 配置文件上传 const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, uploadDir); }, filename: function (req, file, cb) { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() 1E9); cb(null, uniqueSuffix + path.extname(file.originalname)); } }); const upload = multer({ storage: storage }); app.use(cors()); app.use(express.json()); app.use('/uploads', express.static(uploadDir)); // 1. 新增律师档案 app.post('/api/archives', (req, res) => { const { license_number, name, gender, birth_date, id_card, phone, email, law_firm, practice_city, first_issue_date, valid_until } = req.body; const sql = `INSERT INTO lawyer_archives (license_number, name, gender, birth_date, id_card, phone, email, law_firm, practice_city, first_issue_date, valid_until) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; db.run(sql, [ license_number, name, gender, birth_date, id_card, phone, email, law_firm, practice_city, first_issue_date, valid_until ], function(err) { if (err) { res.status(500).json({ error: err.message }); return; } res.json({ id: this.lastID, message: '档案创建成功' }); }); }); // 2. 查询档案列表(带分页和搜索) app.get('/api/archives', (req, res) => { const { page = 1, pageSize = 10, keyword = '' } = req.query; const offset = (page - 1) pageSize; let sql = `SELECT FROM lawyer_archives WHERE 1=1`; let params = []; if (keyword) { sql += ` AND (name LIKE ? OR license_number LIKE ? OR law_firm LIKE ?)`; const likeKeyword = `%${keyword}%`; params = [likeKeyword, likeKeyword, likeKeyword]; } sql += ` ORDER BY updated_at DESC LIMIT ? OFFSET ?`; params.push(parseInt(pageSize), parseInt(offset)); db.all(sql, params, (err, rows) => { if (err) { res.status(500).json({ error: err.message }); return; } // 获取总数 let countSql = `SELECT COUNT() as total FROM lawyer_archives WHERE 1=1`; let countParams = []; if (keyword) { countSql += ` AND (name LIKE ? OR license_number LIKE ? OR law_firm LIKE ?)`; countParams = [`%${keyword}%`, `%${keyword}%`, `%${keyword}%`]; } db.get(countSql, countParams, (countErr, countResult) => { if (countErr) { res.status(500).json({ error: countErr.message }); return; } res.json({ data: rows, total: countResult.total, page: parseInt(page), pageSize: parseInt(pageSize) }); }); }); }); // 3. 上传证件附件 app.post('/api/archives/:id/attachments', upload.array('files', 5), (req, res) => { const archiveId = req.params.id; const files = req.files; if (!files || files.length === 0) { res.status(400).json({ error: '未上传文件' }); return; } const sql = `INSERT INTO archive_attachments (archive_id, file_name, file_path, file_type) VALUES (?, ?, ?, ?)`; const stmt = db.prepare(sql); files.forEach(file => { stmt.run([archiveId, file.originalname, file.filename, file.mimetype]); }); stmt.finalize(); res.json({ message: `成功上传${files.length}个文件` }); }); // 4. 更新年检状态 app.patch('/api/archives/:id/check', (req, res) => { const { annual_check_status, check_notes } = req.body; const sql = `UPDATE lawyer_archives SET annual_check_status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`; db.run(sql, [annual_check_status, req.params.id], function(err) { if (err) { res.status(500).json({ error: err.message }); return; } res.json({ message: '年检状态更新成功' }); }); }); app.listen(PORT, () => { console.log(`后端服务运行在 http://localhost:${PORT}`); }); ```
在backend/package.json中添加启动脚本:
```json { "scripts": { "start": "node app.js", "dev": "nodemon app.js" } } ```
三、前端界面开发
3.1 配置API请求基础模块

在frontend/src目录下创建utils/request.js文件。
```javascript // frontend/src/utils/request.js import axios from 'axios'; const request = axios.create({ baseURL: 'http://localhost:3000/api', timeout: 10000 }); // 请求拦截器 request.interceptors.request.use( config => { return config; }, error => { return Promise.reject(error); } ); // 响应拦截器 request.interceptors.response.use( response => { return response.data; }, error => { return Promise.reject(error); } ); export default request; ```
3.2 开发档案管理主页面
修改frontend/src/views/HomeView.vue(或新建一个组件),实现档案的增删改查。
```vue
律师执业证档案管理
3.3 配置路由
修改frontend/src/router/index.js,添加档案管理路由。
```javascript // frontend/src/router/index.js import { createRouter, createWebHistory } from 'vue-router' import ArchiveManage from '../views/ArchiveManage.vue' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', name: 'archive', component: ArchiveManage } ] }) export default router ```
四、系统部署与使用
4.1 启动后端服务
在终端中进入backend目录,执行以下命令:
```bash cd backend npm run dev ```
服务启动后,访问http://localhost:3000应能看到运行成功的日志。
4.2 启动前端服务
打开另一个终端,进入frontend目录,执行:
```bash cd frontend npm run dev ```
根据终端提示的地址(通常是http://localhost:5173)访问前端界面。