Files
dianming/storage.js
2026-04-25 10:09:16 +08:00

288 lines
8.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 通用数据存储模块
* 支持两种模式:
* 1. 服务器模式通过HTTP API与后端交互
* 2. 本地模式使用localStorage存储数据
*/
class DataStorage {
constructor() {
this.apiBase = this.getApiBase();
this.isServerMode = this.checkServerMode();
console.log(`数据存储模式: ${this.isServerMode ? '服务器模式' : '本地模式'}`);
}
/**
* 检测运行模式
*/
checkServerMode() {
// 如果是通过HTTP服务器访问非file协议则使用服务器模式
return window.location.protocol === 'http:' || window.location.protocol === 'https:';
}
/**
* 获取API基础URL
*/
getApiBase() {
if (window.location.protocol === 'http:' || window.location.protocol === 'https:') {
return `${window.location.protocol}//${window.location.host}/api`;
}
return null;
}
/**
* HTTP请求封装
*/
async httpRequest(url, method = 'GET', data = null) {
const options = {
method: method,
headers: {
'Content-Type': 'application/json'
}
};
if (data && method !== 'GET') {
options.body = JSON.stringify(data);
}
try {
const response = await fetch(url, options);
// 检查响应类型
const contentType = response.headers.get('content-type');
const isJson = contentType && contentType.includes('application/json');
if (!response.ok) {
// 如果响应不是JSON尝试读取文本
if (!isJson) {
const text = await response.text();
throw new Error(`服务器错误 (${response.status}): 返回了非JSON响应`);
}
const result = await response.json();
throw new Error(result.error || `HTTP错误: ${response.status}`);
}
// 检查成功响应是否为JSON
if (!isJson) {
throw new Error('服务器返回了非JSON响应请检查API接口');
}
const result = await response.json();
return result;
} catch (error) {
// 处理网络错误
if (error.message === 'Failed to fetch') {
throw new Error('无法连接到服务器,请确保:\n1. 已启动Node.js服务器\n2. 服务器运行在正确端口\n3. 或使用本地模式直接打开HTML文件');
}
// 处理JSON解析错误
if (error.message.includes('Unexpected token')) {
throw new Error('服务器返回了HTML页面而非JSON数据可能是\n1. API接口不存在\n2. 服务器配置错误\n3. 建议使用本地模式或检查服务器');
}
throw error;
}
}
/**
* 登录
*/
async login(username, password) {
if (this.isServerMode) {
// 服务器模式
const result = await this.httpRequest(`${this.apiBase}/login`, 'POST', { username, password });
return result.data;
} else {
// 本地模式
if (username === 'admin' && password === 'admin123') {
return { token: 'local-token-' + Date.now() };
}
throw new Error('用户名或密码错误');
}
}
/**
* 创建点名名单
*/
async createList(name, members) {
if (this.isServerMode) {
const result = await this.httpRequest(`${this.apiBase}/create-list`, 'POST', { name, members });
return result.data;
} else {
// 本地模式
const lists = this.getLocalLists();
const newList = {
id: Date.now(),
name: name,
code: this.generateCode(),
members: members.map(memberName => ({
name: memberName,
completed: false,
timestamp: null
})),
createdAt: new Date().toLocaleString()
};
lists.push(newList);
localStorage.setItem('rollCallLists', JSON.stringify(lists));
return newList;
}
}
/**
* 获取所有名单
*/
async getLists() {
if (this.isServerMode) {
const result = await this.httpRequest(`${this.apiBase}/get-lists`);
return result.data;
} else {
return this.getLocalLists();
}
}
/**
* 获取单个名单
*/
async getList(code) {
if (this.isServerMode) {
const result = await this.httpRequest(`${this.apiBase}/get-list?code=${code}`);
return result.data;
} else {
const lists = this.getLocalLists();
const list = lists.find(l => l.code === code);
if (!list) {
throw new Error('名单不存在');
}
return list;
}
}
/**
* 更新名单
*/
async updateList(id, name, members) {
if (this.isServerMode) {
const result = await this.httpRequest(`${this.apiBase}/update-list`, 'POST', { id, name, members });
return result.data;
} else {
const lists = this.getLocalLists();
const list = lists.find(l => l.id === id);
if (!list) {
throw new Error('名单不存在');
}
if (name) list.name = name;
if (members && Array.isArray(members)) {
list.members = members.map(memberName => {
const existing = list.members.find(m => m.name === memberName);
return existing || { name: memberName, completed: false, timestamp: null };
});
}
localStorage.setItem('rollCallLists', JSON.stringify(lists));
return list;
}
}
/**
* 删除名单
*/
async deleteList(id) {
if (this.isServerMode) {
const result = await this.httpRequest(`${this.apiBase}/delete-list`, 'POST', { id });
return result.data;
} else {
let lists = this.getLocalLists();
lists = lists.filter(l => l.id !== id);
localStorage.setItem('rollCallLists', JSON.stringify(lists));
return { message: '删除成功' };
}
}
/**
* 点名
*/
async rollCall(code, name) {
if (this.isServerMode) {
const result = await this.httpRequest(`${this.apiBase}/roll-call`, 'POST', { code, name });
return result.data;
} else {
const lists = this.getLocalLists();
const list = lists.find(l => l.code === code);
if (!list) {
throw new Error('名单不存在');
}
const member = list.members.find(m => m.name === name);
if (!member) {
throw new Error('成员不存在');
}
if (member.completed) {
throw new Error('该成员已点名');
}
member.completed = true;
member.timestamp = new Date().toLocaleString();
// 添加记录
const records = this.getLocalRecords();
records.push({
name: name,
listName: list.name,
code: code,
timestamp: member.timestamp
});
localStorage.setItem('rollCallLists', JSON.stringify(lists));
localStorage.setItem('rollCallRecords', JSON.stringify(records));
return { message: '点名成功', member: member };
}
}
/**
* 获取点名记录
*/
async getRecords() {
if (this.isServerMode) {
const result = await this.httpRequest(`${this.apiBase}/get-records`);
return result.data;
} else {
return this.getLocalRecords();
}
}
/**
* 本地模式:获取名单
*/
getLocalLists() {
const data = localStorage.getItem('rollCallLists');
return data ? JSON.parse(data) : [];
}
/**
* 本地模式:获取记录
*/
getLocalRecords() {
const data = localStorage.getItem('rollCallRecords');
return data ? JSON.parse(data) : [];
}
/**
* 生成随机编号
*/
generateCode() {
const length = Math.floor(Math.random() * 7) + 6; // 6-12位
let code = '';
for (let i = 0; i < length; i++) {
code += Math.floor(Math.random() * 10);
}
return code;
}
}
// 创建全局实例
window.dataStorage = new DataStorage();