feat: Add new server binaries and reorder 'Time' column in customer table and CSV export.
This commit is contained in:
parent
9e8c8ec8f2
commit
2b50b3a2f7
@ -107,7 +107,6 @@
|
|||||||
<table id="customerTable">
|
<table id="customerTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>时间</th>
|
|
||||||
<th>客户</th>
|
<th>客户</th>
|
||||||
<th>版本</th>
|
<th>版本</th>
|
||||||
<th>描述</th>
|
<th>描述</th>
|
||||||
@ -116,6 +115,7 @@
|
|||||||
<th>模块</th>
|
<th>模块</th>
|
||||||
<th>状态与进度</th>
|
<th>状态与进度</th>
|
||||||
<th>报告人</th>
|
<th>报告人</th>
|
||||||
|
<th>时间</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
// 登录守卫
|
// 登录守卫
|
||||||
const token = localStorage.getItem('crmToken');
|
const token = localStorage.getItem('crmToken');
|
||||||
if (!token && !window.location.pathname.endsWith('login.html')) {
|
if (!token && !window.location.pathname.endsWith('login.html')) {
|
||||||
@ -201,7 +201,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Navigation event listeners
|
// Navigation event listeners
|
||||||
navItems.forEach(item => {
|
navItems.forEach(item => {
|
||||||
item.addEventListener('click', function(e) {
|
item.addEventListener('click', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const section = this.getAttribute('data-section');
|
const section = this.getAttribute('data-section');
|
||||||
switchSection(section);
|
switchSection(section);
|
||||||
@ -209,12 +209,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Menu toggle for mobile
|
// Menu toggle for mobile
|
||||||
menuToggle.addEventListener('click', function() {
|
menuToggle.addEventListener('click', function () {
|
||||||
sidebar.classList.toggle('open');
|
sidebar.classList.toggle('open');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close sidebar when clicking outside on mobile
|
// Close sidebar when clicking outside on mobile
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function (e) {
|
||||||
if (window.innerWidth <= 768) {
|
if (window.innerWidth <= 768) {
|
||||||
if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) {
|
if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) {
|
||||||
sidebar.classList.remove('open');
|
sidebar.classList.remove('open');
|
||||||
@ -223,39 +223,39 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add Customer button
|
// Add Customer button
|
||||||
addCustomerBtn.addEventListener('click', function() {
|
addCustomerBtn.addEventListener('click', function () {
|
||||||
createModal.style.display = 'block';
|
createModal.style.display = 'block';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Import button
|
// Import button
|
||||||
importBtn.addEventListener('click', function() {
|
importBtn.addEventListener('click', function () {
|
||||||
importModal.style.display = 'block';
|
importModal.style.display = 'block';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close create modal
|
// Close create modal
|
||||||
createModal.querySelector('.close').addEventListener('click', function() {
|
createModal.querySelector('.close').addEventListener('click', function () {
|
||||||
createModal.style.display = 'none';
|
createModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
createModal.querySelector('.cancel-create').addEventListener('click', function() {
|
createModal.querySelector('.cancel-create').addEventListener('click', function () {
|
||||||
createModal.style.display = 'none';
|
createModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close import modal
|
// Close import modal
|
||||||
importModal.querySelector('.close').addEventListener('click', function() {
|
importModal.querySelector('.close').addEventListener('click', function () {
|
||||||
importModal.style.display = 'none';
|
importModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
importModal.querySelector('.cancel-import').addEventListener('click', function() {
|
importModal.querySelector('.cancel-import').addEventListener('click', function () {
|
||||||
importModal.style.display = 'none';
|
importModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close edit modal
|
// Close edit modal
|
||||||
editModal.querySelector('.close').addEventListener('click', function() {
|
editModal.querySelector('.close').addEventListener('click', function () {
|
||||||
editModal.style.display = 'none';
|
editModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
editModal.querySelector('.cancel-edit').addEventListener('click', function() {
|
editModal.querySelector('.cancel-edit').addEventListener('click', function () {
|
||||||
editModal.style.display = 'none';
|
editModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const importFile = document.getElementById('importFile');
|
const importFile = document.getElementById('importFile');
|
||||||
const fileName = document.getElementById('fileName');
|
const fileName = document.getElementById('fileName');
|
||||||
|
|
||||||
importFile.addEventListener('change', function() {
|
importFile.addEventListener('change', function () {
|
||||||
if (this.files.length > 0) {
|
if (this.files.length > 0) {
|
||||||
fileName.textContent = this.files[0].name;
|
fileName.textContent = this.files[0].name;
|
||||||
} else {
|
} else {
|
||||||
@ -272,14 +272,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Customer filter change event
|
// Customer filter change event
|
||||||
customerFilter.addEventListener('change', function() {
|
customerFilter.addEventListener('change', function () {
|
||||||
selectedCustomerFilter = this.value;
|
selectedCustomerFilter = this.value;
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
applyAllCustomerFilters();
|
applyAllCustomerFilters();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (customerSearchInput) {
|
if (customerSearchInput) {
|
||||||
customerSearchInput.addEventListener('input', function() {
|
customerSearchInput.addEventListener('input', function () {
|
||||||
customerSearchQuery = (this.value || '').trim();
|
customerSearchQuery = (this.value || '').trim();
|
||||||
if (currentSection === 'customer') {
|
if (currentSection === 'customer') {
|
||||||
currentPage = 1;
|
currentPage = 1;
|
||||||
@ -289,18 +289,18 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply date filter for dashboard
|
// Apply date filter for dashboard
|
||||||
document.getElementById('applyFilters').addEventListener('click', function() {
|
document.getElementById('applyFilters').addEventListener('click', function () {
|
||||||
const startDate = document.getElementById('startDate').value;
|
const startDate = document.getElementById('startDate').value;
|
||||||
const endDate = document.getElementById('endDate').value;
|
const endDate = document.getElementById('endDate').value;
|
||||||
applyDateFilter(startDate, endDate);
|
applyDateFilter(startDate, endDate);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Chart title change events
|
// Chart title change events
|
||||||
document.getElementById('statusChartTitle').addEventListener('input', function() {
|
document.getElementById('statusChartTitle').addEventListener('input', function () {
|
||||||
applyDateFilter(document.getElementById('startDate').value, document.getElementById('endDate').value);
|
applyDateFilter(document.getElementById('startDate').value, document.getElementById('endDate').value);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('typeChartTitle').addEventListener('input', function() {
|
document.getElementById('typeChartTitle').addEventListener('input', function () {
|
||||||
applyDateFilter(document.getElementById('startDate').value, document.getElementById('endDate').value);
|
applyDateFilter(document.getElementById('startDate').value, document.getElementById('endDate').value);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -438,7 +438,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
function exportCustomersToCsv(customers) {
|
function exportCustomersToCsv(customers) {
|
||||||
const header = [
|
const header = [
|
||||||
'时间',
|
|
||||||
'客户',
|
'客户',
|
||||||
'版本',
|
'版本',
|
||||||
'描述',
|
'描述',
|
||||||
@ -446,13 +445,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
'类型',
|
'类型',
|
||||||
'模块',
|
'模块',
|
||||||
'状态与进度',
|
'状态与进度',
|
||||||
'报告人'
|
'报告人',
|
||||||
|
'时间'
|
||||||
].map(toCsvCell).join(',');
|
].map(toCsvCell).join(',');
|
||||||
|
|
||||||
const lines = customers.map(c => {
|
const lines = customers.map(c => {
|
||||||
const date = normalizeDateValue(c.customerName) || (c.customerName || '');
|
const date = normalizeDateValue(c.customerName) || (c.customerName || '');
|
||||||
const cells = [
|
const cells = [
|
||||||
date,
|
|
||||||
c.intendedProduct || '',
|
c.intendedProduct || '',
|
||||||
c.version || '',
|
c.version || '',
|
||||||
c.description || '',
|
c.description || '',
|
||||||
@ -460,7 +459,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
c.type || '',
|
c.type || '',
|
||||||
c.module || '',
|
c.module || '',
|
||||||
c.statusProgress || '',
|
c.statusProgress || '',
|
||||||
c.reporter || ''
|
c.reporter || '',
|
||||||
|
date
|
||||||
];
|
];
|
||||||
return cells.map(toCsvCell).join(',');
|
return cells.map(toCsvCell).join(',');
|
||||||
});
|
});
|
||||||
@ -563,7 +563,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const date = customer.customerName || '';
|
const date = customer.customerName || '';
|
||||||
|
|
||||||
const fields = [
|
const fields = [
|
||||||
{ value: date, name: 'date' },
|
|
||||||
{ value: customer.intendedProduct || '', name: 'intendedProduct' },
|
{ value: customer.intendedProduct || '', name: 'intendedProduct' },
|
||||||
{ value: customer.version || '', name: 'version' },
|
{ value: customer.version || '', name: 'version' },
|
||||||
{ value: customer.description || '', name: 'description' },
|
{ value: customer.description || '', name: 'description' },
|
||||||
@ -571,7 +570,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
{ value: customer.type || '', name: 'type' },
|
{ value: customer.type || '', name: 'type' },
|
||||||
{ value: customer.module || '', name: 'module' },
|
{ value: customer.module || '', name: 'module' },
|
||||||
{ value: customer.statusProgress || '', name: 'statusProgress' },
|
{ value: customer.statusProgress || '', name: 'statusProgress' },
|
||||||
{ value: customer.reporter || '', name: 'reporter' }
|
{ value: customer.reporter || '', name: 'reporter' },
|
||||||
|
{ value: date, name: 'date' }
|
||||||
];
|
];
|
||||||
|
|
||||||
fields.forEach(field => {
|
fields.forEach(field => {
|
||||||
@ -604,14 +604,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
checkTextOverflow();
|
checkTextOverflow();
|
||||||
|
|
||||||
document.querySelectorAll('.edit-btn').forEach(btn => {
|
document.querySelectorAll('.edit-btn').forEach(btn => {
|
||||||
btn.addEventListener('click', function() {
|
btn.addEventListener('click', function () {
|
||||||
const customerId = this.getAttribute('data-id');
|
const customerId = this.getAttribute('data-id');
|
||||||
openEditModal(customerId);
|
openEditModal(customerId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelectorAll('.delete-btn').forEach(btn => {
|
document.querySelectorAll('.delete-btn').forEach(btn => {
|
||||||
btn.addEventListener('click', function() {
|
btn.addEventListener('click', function () {
|
||||||
const customerId = this.getAttribute('data-id');
|
const customerId = this.getAttribute('data-id');
|
||||||
deleteCustomer(customerId);
|
deleteCustomer(customerId);
|
||||||
});
|
});
|
||||||
@ -673,7 +673,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create customer
|
// Create customer
|
||||||
createCustomerForm.addEventListener('submit', async function(e) {
|
createCustomerForm.addEventListener('submit', async function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const formData = {
|
const formData = {
|
||||||
@ -712,7 +712,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Import customers
|
// Import customers
|
||||||
importFileForm.addEventListener('submit', async function(e) {
|
importFileForm.addEventListener('submit', async function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@ -771,7 +771,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('click', function(e) {
|
window.addEventListener('click', function (e) {
|
||||||
if (e.target === createModal) {
|
if (e.target === createModal) {
|
||||||
createModal.style.display = 'none';
|
createModal.style.display = 'none';
|
||||||
}
|
}
|
||||||
@ -784,7 +784,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Update customer
|
// Update customer
|
||||||
editCustomerForm.addEventListener('submit', async function(e) {
|
editCustomerForm.addEventListener('submit', async function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const customerId = document.getElementById('editCustomerId').value;
|
const customerId = document.getElementById('editCustomerId').value;
|
||||||
@ -1050,14 +1050,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Chart field select event listener
|
// Chart field select event listener
|
||||||
document.getElementById('chartFieldSelect').addEventListener('change', function() {
|
document.getElementById('chartFieldSelect').addEventListener('change', function () {
|
||||||
const startDate = document.getElementById('startDate').value;
|
const startDate = document.getElementById('startDate').value;
|
||||||
const endDate = document.getElementById('endDate').value;
|
const endDate = document.getElementById('endDate').value;
|
||||||
applyDateFilter(startDate, endDate);
|
applyDateFilter(startDate, endDate);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Type chart field select event listener
|
// Type chart field select event listener
|
||||||
document.getElementById('typeChartFieldSelect').addEventListener('change', function() {
|
document.getElementById('typeChartFieldSelect').addEventListener('change', function () {
|
||||||
const startDate = document.getElementById('startDate').value;
|
const startDate = document.getElementById('startDate').value;
|
||||||
const endDate = document.getElementById('endDate').value;
|
const endDate = document.getElementById('endDate').value;
|
||||||
applyDateFilter(startDate, endDate);
|
applyDateFilter(startDate, endDate);
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crm-go/models"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"crm-go/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CustomerStorage interface {
|
type CustomerStorage interface {
|
||||||
@ -43,7 +43,21 @@ func (cs *customerStorage) GetAllCustomers() ([]models.Customer, error) {
|
|||||||
cs.mutex.RLock()
|
cs.mutex.RLock()
|
||||||
defer cs.mutex.RUnlock()
|
defer cs.mutex.RUnlock()
|
||||||
|
|
||||||
return cs.LoadCustomers()
|
customers, err := cs.LoadCustomers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by CreatedAt in descending order (newest first)
|
||||||
|
for i := 0; i < len(customers)-1; i++ {
|
||||||
|
for j := i + 1; j < len(customers); j++ {
|
||||||
|
if customers[i].CreatedAt.Before(customers[j].CreatedAt) {
|
||||||
|
customers[i], customers[j] = customers[j], customers[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return customers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *customerStorage) GetCustomerByID(id string) (*models.Customer, error) {
|
func (cs *customerStorage) GetCustomerByID(id string) (*models.Customer, error) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user