First
This commit is contained in:
287
storage.js
Normal file
287
storage.js
Normal file
@@ -0,0 +1,287 @@
|
||||
/**
|
||||
* 通用数据存储模块
|
||||
* 支持两种模式:
|
||||
* 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();
|
||||
Reference in New Issue
Block a user