@@ -0,0 +1,156 @@
|
||||
# 单据查询系统 - Supabase 配置指南
|
||||
|
||||
## 1. Supabase 数据库设置
|
||||
|
||||
### 1.1 创建数据表
|
||||
|
||||
在您的 Supabase 项目中执行以下 SQL 语句创建 `tickets` 表:
|
||||
|
||||
```sql
|
||||
-- 创建单据表
|
||||
CREATE TABLE tickets (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
ticket_number VARCHAR(50) NOT NULL UNIQUE, -- 编号
|
||||
customer_name VARCHAR(100), -- 姓名
|
||||
reason TEXT, -- 事由
|
||||
result TEXT, -- 处理结果
|
||||
amount DECIMAL(10, 2) DEFAULT 0, -- 金额
|
||||
issuer VARCHAR(100), -- 开具人
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), -- 日期
|
||||
processed BOOLEAN DEFAULT FALSE, -- 处理状态
|
||||
-- 兼容原字段,保留以便现有代码不报错
|
||||
ticket_type VARCHAR(20) DEFAULT 'other',
|
||||
status VARCHAR(20) DEFAULT 'pending',
|
||||
contact_phone VARCHAR(20),
|
||||
email VARCHAR(100),
|
||||
remarks TEXT,
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 创建索引以提高查询性能
|
||||
CREATE INDEX idx_ticket_number ON tickets(ticket_number);
|
||||
CREATE INDEX idx_ticket_type ON tickets(ticket_type);
|
||||
CREATE INDEX idx_status ON tickets(status);
|
||||
CREATE INDEX idx_created_at ON tickets(created_at);
|
||||
|
||||
-- 创建自动更新时间戳的触发器
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER update_tickets_updated_at
|
||||
BEFORE UPDATE ON tickets
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
```
|
||||
|
||||
### 1.2 设置行级安全策略(RLS)
|
||||
|
||||
```sql
|
||||
-- 启用行级安全
|
||||
ALTER TABLE tickets ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 允许所有人读取(根据需要调整)
|
||||
CREATE POLICY "Allow public read access" ON tickets
|
||||
FOR SELECT USING (true);
|
||||
|
||||
-- 允许认证用户插入和更新
|
||||
CREATE POLICY "Allow authenticated insert" ON tickets
|
||||
FOR INSERT TO authenticated WITH CHECK (true);
|
||||
|
||||
CREATE POLICY "Allow authenticated update" ON tickets
|
||||
FOR UPDATE TO authenticated WITH CHECK (true);
|
||||
```
|
||||
|
||||
## 2. 获取 Supabase 凭证
|
||||
|
||||
1. 登录 [Supabase](https://supabase.com)
|
||||
2. 选择您的项目
|
||||
3. 进入 **Settings** > **API**
|
||||
4. 复制以下内容:
|
||||
- **Project URL** (替换代码中的 `YOUR_SUPABASE_URL`)
|
||||
- **anon/public key** (替换代码中的 `YOUR_SUPABASE_ANON_KEY`)
|
||||
|
||||
## 3. 配置前端代码
|
||||
|
||||
打开 `index.html` 和 `detail.html`,找到以下代码:
|
||||
|
||||
```javascript
|
||||
const SUPABASE_URL = 'YOUR_SUPABASE_URL';
|
||||
const SUPABASE_ANON_KEY = 'YOUR_SUPABASE_ANON_KEY';
|
||||
```
|
||||
|
||||
替换为您从 Supabase 获取的实际值。
|
||||
|
||||
## 4. Vercel 部署
|
||||
|
||||
### 4.1 安装 Vercel CLI(可选)
|
||||
|
||||
```bash
|
||||
npm install -g vercel
|
||||
```
|
||||
|
||||
### 4.2 部署步骤
|
||||
|
||||
1. **使用 Vercel Dashboard 部署**:
|
||||
- 访问 [vercel.com](https://vercel.com)
|
||||
- 导入您的 Git 仓库
|
||||
- Vercel 会自动检测 HTML 文件并部署
|
||||
|
||||
2. **使用 CLI 部署**:
|
||||
```bash
|
||||
cd ticket
|
||||
vercel
|
||||
```
|
||||
|
||||
### 4.3 环境变量(推荐)
|
||||
|
||||
为安全起见,建议使用环境变量:
|
||||
|
||||
1. 在 Vercel 项目设置中添加环境变量:
|
||||
- `NEXT_PUBLIC_SUPABASE_URL`
|
||||
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
|
||||
|
||||
2. 修改代码使用环境变量:
|
||||
```javascript
|
||||
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || 'YOUR_SUPABASE_URL';
|
||||
const SUPABASE_ANON_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'YOUR_SUPABASE_ANON_KEY';
|
||||
```
|
||||
|
||||
## 5. 测试数据
|
||||
|
||||
您可以插入一些测试数据:
|
||||
|
||||
```sql
|
||||
INSERT INTO tickets (ticket_number, ticket_type, amount, status, customer_name, contact_phone, email, remarks) VALUES
|
||||
('TKT20260328001', 'invoice', 1500.00, 'completed', '张三', '13800138000', 'zhangsan@example.com', '办公用品采购'),
|
||||
('TKT20260328002', 'receipt', 800.00, 'processing', '李四', '13900139000', 'lisi@example.com', '咨询服务费'),
|
||||
('TKT20260328003', 'order', 2500.00, 'pending', '王五', '13700137000', 'wangwu@example.com', '设备租赁'),
|
||||
('TKT20260328004', 'invoice', 3200.00, 'completed', '赵六', '13600136000', 'zhaoliu@example.com', '软件授权费'),
|
||||
('TKT20260328005', 'other', 500.00, 'cancelled', '钱七', '13500135000', 'qianqi@example.com', '其他费用');
|
||||
```
|
||||
|
||||
## 6. 功能说明
|
||||
|
||||
- ✅ 按单据编号模糊查询
|
||||
- ✅ 按单据类型筛选
|
||||
- ✅ 按日期范围查询
|
||||
- ✅ 查看单据详情
|
||||
- ✅ 打印单据功能
|
||||
- ✅ 响应式设计,支持移动端
|
||||
|
||||
## 7. 注意事项
|
||||
|
||||
1. **安全性**:生产环境务必使用环境变量存储敏感信息
|
||||
2. **权限控制**:根据实际需求调整 RLS 策略
|
||||
3. **性能优化**:大数据量时考虑分页和更多索引
|
||||
4. **错误处理**:已包含基本的错误提示机制
|
||||
|
||||
## 8. 自定义样式
|
||||
|
||||
如需修改样式,可以编辑 HTML 文件中的 `<style>` 部分,或创建独立的 CSS 文件。
|
||||
@@ -0,0 +1,304 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>单据详情</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../css/style.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||||
<style>
|
||||
.detail-container {
|
||||
max-width: 900px;
|
||||
margin: 40px auto;
|
||||
padding: 30px;
|
||||
background: white;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
color: #4CAF50;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
border-bottom: 2px solid #4CAF50;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
color: #4CAF50;
|
||||
margin-bottom: 15px;
|
||||
border-left: 3px solid #4CAF50;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
padding: 15px;
|
||||
background: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 5px 15px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.status-processing {
|
||||
background-color: #cce5ff;
|
||||
color: #004085;
|
||||
}
|
||||
|
||||
.status-completed {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.status-cancelled {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background-color: #fee;
|
||||
color: #c33;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="detail-container">
|
||||
<a href="index.html" class="back-link">← 返回查询页面</a>
|
||||
|
||||
<div id="detailContent">
|
||||
<div class="loading">正在加载详情...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 初始化 Supabase 客户端
|
||||
const SUPABASE_URL = 'YOUR_SUPABASE_URL';
|
||||
const SUPABASE_ANON_KEY = 'YOUR_SUPABASE_ANON_KEY';
|
||||
|
||||
const supabase = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
||||
|
||||
async function loadTicketDetail() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const ticketId = urlParams.get('id');
|
||||
|
||||
if (!ticketId) {
|
||||
document.getElementById('detailContent').innerHTML = `
|
||||
<div class="error-message">
|
||||
错误:未提供单据 ID
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { data: ticket, error } = await supabase
|
||||
.from('tickets')
|
||||
.select('*')
|
||||
.eq('id', ticketId)
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
if (!ticket) {
|
||||
document.getElementById('detailContent').innerHTML = `
|
||||
<div class="error-message">
|
||||
未找到该单据信息
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
displayTicketDetail(ticket);
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载详情失败:', error);
|
||||
document.getElementById('detailContent').innerHTML = `
|
||||
<div class="error-message">
|
||||
加载失败:${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function displayTicketDetail(ticket) {
|
||||
const contentDiv = document.getElementById('detailContent');
|
||||
|
||||
contentDiv.innerHTML = `
|
||||
<div class="detail-header">
|
||||
<h1 class="detail-title">单据详情 - ${ticket.ticket_number || ''} / ${ticket.customer_name || ''}</h1>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<h3 class="section-title">基本信息</h3>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">编号</div>
|
||||
<div class="info-value">${ticket.ticket_number || '无'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">姓名</div>
|
||||
<div class="info-value">${ticket.customer_name || '未填写'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">事由</div>
|
||||
<div class="info-value">${ticket.reason || '无'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">处理结果</div>
|
||||
<div class="info-value">${ticket.result || '无'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">金额</div>
|
||||
<div class="info-value">¥${ticket.amount?.toFixed(2) || '0.00'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">开具人</div>
|
||||
<div class="info-value">${ticket.issuer || '未填写'}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">日期</div>
|
||||
<div class="info-value">${formatDate(ticket.created_at || ticket.date)}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">处理状态</div>
|
||||
<div class="info-value">${ticket.processed ? '已处理' : '未处理'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-primary" onclick="window.print()">打印单据</button>
|
||||
<button class="btn btn-secondary" onclick="history.back()">返回列表</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function getTicketTypeName(type) {
|
||||
const types = {
|
||||
'invoice': '发票',
|
||||
'receipt': '收据',
|
||||
'order': '订单',
|
||||
'other': '其他'
|
||||
};
|
||||
return types[type] || type;
|
||||
}
|
||||
|
||||
function getStatusName(status) {
|
||||
const statusMap = {
|
||||
'pending': '待处理',
|
||||
'processing': '处理中',
|
||||
'completed': '已完成',
|
||||
'cancelled': '已取消'
|
||||
};
|
||||
return statusMap[status] || status;
|
||||
}
|
||||
|
||||
function formatDate(dateString) {
|
||||
if (!dateString) return '暂无';
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
// 页面加载时获取详情
|
||||
document.addEventListener('DOMContentLoaded', loadTicketDetail);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,240 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>单据查询</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../css/style.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
|
||||
<style>
|
||||
.ticket-container {
|
||||
max-width: 800px;
|
||||
margin: 40px auto;
|
||||
padding: 30px;
|
||||
background: white;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.result-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.result-table th,
|
||||
.result-table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.result-table th {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.result-table tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.no-result {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background-color: #fee;
|
||||
color: #c33;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
color: #4CAF50;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="ticket-container">
|
||||
<a href="../index.html" class="back-link">← 返回首页</a>
|
||||
|
||||
<h1 style="text-align: center; margin-bottom: 30px;">单据查询</h1>
|
||||
|
||||
<div class="search-form">
|
||||
<div class="form-group">
|
||||
<label for="searchTerm">编号 / 姓名</label>
|
||||
<input type="text" id="searchTerm" class="form-control" placeholder="请输入单据编号或姓名">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="dateFrom">开始日期</label>
|
||||
<input type="date" id="dateFrom" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="dateTo">结束日期</label>
|
||||
<input type="date" id="dateTo" class="form-control">
|
||||
</div>
|
||||
|
||||
<button onclick="searchTickets()" class="btn">查询</button>
|
||||
</div>
|
||||
|
||||
<div id="searchResults">
|
||||
<!-- 查询结果将在这里显示 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 1. 只声明一次变量
|
||||
const SUPABASE_URL = 'https://chixssrphfgxvqqigkzo.supabase.co';
|
||||
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNoaXhzc3JwaGZneHZxcWlna3pvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQ2OTE0OTEsImV4cCI6MjA5MDI2NzQ5MX0.Az_Ew2J2zdOMcSV0UNAjBS-LPqGpqhsaN4IyZ5R7iqU';
|
||||
|
||||
// 2. 初始化客户端 (确保只在这里初始化一次)
|
||||
const supabase = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
||||
|
||||
// 3. 定义查询函数
|
||||
async function searchTickets() {
|
||||
const searchTerm = document.getElementById('searchTerm').value.trim();
|
||||
const dateFrom = document.getElementById('dateFrom').value;
|
||||
const dateTo = document.getElementById('dateTo').value;
|
||||
|
||||
const resultsDiv = document.getElementById('searchResults');
|
||||
resultsDiv.innerHTML = '<div class="loading">正在查询...</div>';
|
||||
|
||||
try {
|
||||
let query = supabase.from('tickets').select('*');
|
||||
|
||||
if (searchTerm) {
|
||||
const escapedTerm = searchTerm.replace(/%/g, '\\%').replace(/_/g, '\\_');
|
||||
query = query.or(`ticket_number.ilike.%${escapedTerm}%,customer_name.ilike.%${escapedTerm}%`);
|
||||
}
|
||||
|
||||
if (dateFrom) query = query.gte('created_at', dateFrom);
|
||||
if (dateTo) query = query.lte('created_at', dateTo);
|
||||
|
||||
const { data, error } = await query;
|
||||
if (error) throw error;
|
||||
|
||||
displayResults(data);
|
||||
|
||||
} catch (error) {
|
||||
console.error('查询错误:', error);
|
||||
resultsDiv.innerHTML = `<div class="error-message">查询失败:${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 定义结果显示函数
|
||||
function displayResults(tickets) {
|
||||
const resultsDiv = document.getElementById('searchResults');
|
||||
if (!tickets || tickets.length === 0) {
|
||||
resultsDiv.innerHTML = '<div class="no-result">未找到符合条件的单据</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>编号</th><th>姓名</th><th>事由</th><th>金额</th><th>日期</th><th>状态</th><th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
tickets.forEach(ticket => {
|
||||
html += `
|
||||
<tr>
|
||||
<td>${ticket.ticket_number || ''}</td>
|
||||
<td>${ticket.customer_name || ''}</td>
|
||||
<td>${ticket.reason || ''}</td>
|
||||
<td>¥${Number(ticket.amount || 0).toFixed(2)}</td>
|
||||
<td>${formatDate(ticket.created_at)}</td>
|
||||
<td>${ticket.processed ? '已处理' : '未处理'}</td>
|
||||
<td><button class="btn" onclick="viewDetail('${ticket.id}')">详情</button></td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
html += '</tbody></table>';
|
||||
resultsDiv.innerHTML = html;
|
||||
}
|
||||
|
||||
function formatDate(dateString) {
|
||||
if (!dateString) return '';
|
||||
return new Date(dateString).toLocaleDateString('zh-CN');
|
||||
}
|
||||
|
||||
function viewDetail(ticketId) {
|
||||
window.location.href = `detail.html?id=${ticketId}`;
|
||||
}
|
||||
|
||||
// 5. 绑定回车键
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const input = document.getElementById('searchTerm');
|
||||
if (input) {
|
||||
input.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') searchTickets();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user