数字档案系统界面:从零构建可落地的现代化管理后台

一、技术栈与项目初始化

我们使用Vue 3 + TypeScript + Element Plus作为前端技术栈,Node.js + Express作为后端服务,数据库采用PostgreSQL。这个组合能确保开发效率、类型安全与UI一致性。

1.1 前端项目初始化

打开命令行,执行以下命令创建项目:

npm create vue@latest digital-archive-frontend

在创建向导中,依次选择:TypeScript、JSX、Vue Router、Pinia、ESLint、Prettier。项目创建完成后,进入目录并安装Element Plus:

cd digital-archive-frontend

npm install element-plus @element-plus/icons-vue

src/main.ts中全局引入Element Plus:

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('app')

1.2 后端项目初始化

新建digital-archive-backend目录,执行:

npm init -y

npm install express pg cors dotenv

数字档案系统界面:从零构建可落地的现代化管理后台

npm install -D typescript @types/node @types/express @types/cors nodemon

创建tsconfig.json

{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}

package.json的scripts中添加:"dev": "nodemon src/index.ts"

二、数据库设计与连接

2.1 PostgreSQL表结构

登录PostgreSQL,创建digital_archive数据库,然后执行以下SQL:

CREATE TABLE archives (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
category VARCHAR(100) NOT NULL,
file_size INTEGER,
upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(50) DEFAULT 'active',
tags TEXT[],
storage_path VARCHAR(500) NOT NULL
);
CREATE INDEX idx_archive_category ON archives(category);
CREATE INDEX idx_archive_status ON archives(status);

2.2 后端数据库连接配置

创建.env文件:

DB_HOST=localhost
DB_PORT=5432
DB_NAME=digital_archive
DB_USER=your_username
DB_PASSWORD=your_password
PORT=3001

创建src/db.ts

import { Pool } from 'pg';
import dotenv from 'dotenv';
dotenv.config();
const pool = new Pool({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
});
export default pool;

三、核心界面组件开发

3.1 档案列表页

创建src/views/ArchiveList.vue



3.2 档案上传组件

创建src/components/UploadDialog.vue


四、后端API接口实现

4.1 文件上传接口

安装multer处理文件上传:npm install multer @types/multer。创建src/routes/upload.ts

import express from 'express'
import multer from 'multer'
import path from 'path'
import fs from 'fs'
const router = express.Router()
const uploadDir = 'uploads'
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir)
}
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, uploadDir)
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random()  1E9)
cb(null, uniqueSuffix + path.extname(file.originalname))
}
})
const upload = multer({
storage,
limits: { fileSize: 50  1024  1024 } // 50MB
})
router.post('/', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).json({ success: false, message: '没有上传文件' })
}
res.json({
success: true,
file_path: req.file.filename,
original_name: req.file.originalname,
size: req.file.size
})
})
export default router

4.2 档案管理接口

创建src/routes/archives.ts

import express from 'express'
import pool from '../db'
const router = express.Router()
// 获取档案列表
router.get('/', async (req, res) => {
try {
const { page = 1, limit = 10, search = '', category = '' } = req.query
const offset = (Number(page) - 1)  Number(limit)
let query = 'SELECT  FROM archives WHERE 1=1'
const params: any[] = []
if (search) {
query += ' AND (title ILIKE $1 OR $2 = ANY(tags))'
params.push(`%${search}%`, search)
}
if (category) {
const paramIndex = params.length + 1
query += ` AND category = $${paramIndex}`
params.push(category)
}
query += ` ORDER BY upload_time DESC LIMIT $${params.length + 1} OFFSET $${params.length + 2}`
params.push(Number(limit), offset)
const result = await pool.query(query, params)
// 获取总数
const countQuery = 'SELECT COUNT() FROM archives WHERE 1=1' +
(search ? ' AND (title ILIKE $1 OR $2 = ANY(tags))' : '') +
(category ? ' AND category = $3' : '')
const countParams = search ? [`%${search}%`, search] : []
if (category) countParams.push(category)
const countResult = await pool.query(countQuery, countParams)
res.json({
success: true,
data: result.rows,
total: parseInt(countResult.rows[0].count),
page: Number(page),
limit: Number(limit)
})
} catch (error) {
console.error(error)
res.status(500).json({ success: false, message: '服务器错误' })
}
})
// 创建档案
router.post('/', async (req, res) => {
try {
const { title, category, tags, file_path } = req.body
const result = await pool.query(
'INSERT INTO archives (title, category, tags, storage_path) VALUES ($1, $2, $3, $4) RETURNING ',
[title, category, tags, file_path]
)
res.json({ success: true, data: result.rows[0] })
} catch (error) {
console.error(error)
res.status(500).json({ success: false, message: '创建失败' })
}
})
// 删除档案
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params
await pool.query('DELETE FROM archives WHERE id = $1', [id])
res.json({ success: true })
} catch (error) {
console.error(error)
res.status(500).json({ success            
AI咨询
热线电话

028-85154420

15388110056

全国售前咨询电话

扫码咨询
安答联动微信公众号二维码

微信扫码关注安答联动

申请试用
热线电话
申请试用

安答联动档案管理系统