fix_index
This commit is contained in:
parent
c8f6341bc9
commit
bc0a84511c
@ -40,51 +40,21 @@ func main() {
|
||||
}()
|
||||
|
||||
// Start trial expiry checker in background
|
||||
// Workday schedule: Monday-Friday, 11:00 AM and 5:00 PM
|
||||
trialChecker := services.NewTrialExpiryChecker(feishuWebhook)
|
||||
go func() {
|
||||
// Check immediately on startup
|
||||
trialPeriods, err := trialPeriodStorage.GetAllTrialPeriods()
|
||||
if err == nil {
|
||||
customers, err := customerStorage.GetAllCustomers()
|
||||
if err == nil {
|
||||
// Create customer name map
|
||||
customersMap := make(map[string]string)
|
||||
for _, c := range customers {
|
||||
customersMap[c.ID] = c.CustomerName
|
||||
}
|
||||
|
||||
// Convert to services.TrialPeriod type
|
||||
serviceTrialPeriods := make([]services.TrialPeriod, len(trialPeriods))
|
||||
for i, tp := range trialPeriods {
|
||||
serviceTrialPeriods[i] = services.TrialPeriod{
|
||||
ID: tp.ID,
|
||||
CustomerName: tp.CustomerName,
|
||||
StartTime: tp.StartTime,
|
||||
EndTime: tp.EndTime,
|
||||
CreatedAt: tp.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
if err := trialChecker.CheckTrialPeriodsAndNotify(serviceTrialPeriods, customersMap); err != nil {
|
||||
log.Printf("Error checking trial expiry: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then check once per day at 10:00 AM
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
// Helper function to check and send trial expiry notifications
|
||||
checkTrialExpiry := func() {
|
||||
trialPeriods, err := trialPeriodStorage.GetAllTrialPeriods()
|
||||
if err != nil {
|
||||
log.Printf("Error loading trial periods for expiry check: %v", err)
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
customers, err := customerStorage.GetAllCustomers()
|
||||
if err != nil {
|
||||
log.Printf("Error loading customers for trial check: %v", err)
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
// Create customer name map
|
||||
@ -109,6 +79,51 @@ func main() {
|
||||
log.Printf("Error checking trial expiry: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if it's a workday (Monday-Friday)
|
||||
isWorkday := func(t time.Time) bool {
|
||||
weekday := t.Weekday()
|
||||
return weekday >= time.Monday && weekday <= time.Friday
|
||||
}
|
||||
|
||||
// Helper function to check if current time matches notification time (11:00 or 17:00)
|
||||
isNotificationTime := func(t time.Time) bool {
|
||||
hour := t.Hour()
|
||||
minute := t.Minute()
|
||||
// Check for 11:00 AM or 5:00 PM (17:00)
|
||||
return (hour == 11 || hour == 17) && minute == 0
|
||||
}
|
||||
|
||||
// Track last notification time to prevent duplicate sends
|
||||
var lastNotificationTime time.Time
|
||||
|
||||
// Check immediately on startup (only on workdays)
|
||||
now := time.Now()
|
||||
if isWorkday(now) {
|
||||
log.Println("Trial expiry checker: Running initial check on startup...")
|
||||
checkTrialExpiry()
|
||||
lastNotificationTime = now
|
||||
}
|
||||
|
||||
// Check every minute for scheduled notification times
|
||||
// Workdays: Monday-Friday, 11:00 AM and 5:00 PM
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
now := time.Now()
|
||||
|
||||
// Only send on workdays at 11:00 AM or 5:00 PM
|
||||
if isWorkday(now) && isNotificationTime(now) {
|
||||
// Ensure we don't send duplicate notifications for the same time slot
|
||||
if lastNotificationTime.Hour() != now.Hour() ||
|
||||
lastNotificationTime.Day() != now.Day() ||
|
||||
lastNotificationTime.Month() != now.Month() {
|
||||
log.Printf("Trial expiry checker: Sending scheduled notification at %s", now.Format("2006-01-02 15:04"))
|
||||
checkTrialExpiry()
|
||||
lastNotificationTime = now
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Enable CORS manually
|
||||
|
||||
@ -358,7 +358,7 @@
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3 id="completedTasks">0</h3>
|
||||
<p>已完成</p>
|
||||
<p>已成交</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card stat-card-highlight">
|
||||
@ -873,6 +873,126 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal for adding follow-up -->
|
||||
<div id="addFollowupModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3><i class="fas fa-plus-circle"></i> 添加跟进记录</h3>
|
||||
<span class="close">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addFollowupModalForm">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="addFollowupCustomerName">客户名称 <span style="color: red;">*</span></label>
|
||||
<select id="addFollowupCustomerName" name="customerName" required>
|
||||
<option value="">请选择客户</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="addFollowupDealStatus">成交状态 <span style="color: red;">*</span></label>
|
||||
<select id="addFollowupDealStatus" name="dealStatus" required>
|
||||
<option value="">请选择</option>
|
||||
<option value="未成交">未成交</option>
|
||||
<option value="已成交">已成交</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="addFollowupCustomerLevel">客户级别 <span style="color: red;">*</span></label>
|
||||
<select id="addFollowupCustomerLevel" name="customerLevel" required>
|
||||
<option value="">请选择</option>
|
||||
<option value="A">A级 (重点客户)</option>
|
||||
<option value="B">B级 (潜在客户)</option>
|
||||
<option value="C">C级 (一般客户)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="addFollowupIndustry">客户行业 <span style="color: red;">*</span></label>
|
||||
<input type="text" id="addFollowupIndustry" name="industry" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="addFollowupTime">跟进时间 <span style="color: red;">*</span></label>
|
||||
<input type="datetime-local" id="addFollowupTime" name="followUpTime" required>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fas fa-save"></i>
|
||||
创建
|
||||
</button>
|
||||
<button type="button" class="btn-secondary cancel-add-followup">
|
||||
<i class="fas fa-times"></i>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal for editing follow-up -->
|
||||
<div id="editFollowupModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3><i class="fas fa-edit"></i> 编辑跟进记录</h3>
|
||||
<span class="close">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editFollowupForm">
|
||||
<input type="hidden" id="editFollowupId">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="editFollowupCustomerName">客户名称 <span style="color: red;">*</span></label>
|
||||
<select id="editFollowupCustomerName" name="customerName" required>
|
||||
<option value="">请选择客户</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editFollowupDealStatus">成交状态 <span style="color: red;">*</span></label>
|
||||
<select id="editFollowupDealStatus" name="dealStatus" required>
|
||||
<option value="">请选择</option>
|
||||
<option value="未成交">未成交</option>
|
||||
<option value="已成交">已成交</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="editFollowupCustomerLevel">客户级别 <span style="color: red;">*</span></label>
|
||||
<select id="editFollowupCustomerLevel" name="customerLevel" required>
|
||||
<option value="">请选择</option>
|
||||
<option value="A">A级 (重点客户)</option>
|
||||
<option value="B">B级 (潜在客户)</option>
|
||||
<option value="C">C级 (一般客户)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editFollowupIndustry">客户行业 <span style="color: red;">*</span></label>
|
||||
<input type="text" id="editFollowupIndustry" name="industry" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editFollowupTime">跟进时间 <span style="color: red;">*</span></label>
|
||||
<input type="datetime-local" id="editFollowupTime" name="followUpTime" required>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fas fa-save"></i>
|
||||
更新
|
||||
</button>
|
||||
<button type="button" class="btn-secondary cancel-edit-followup">
|
||||
<i class="fas fa-times"></i>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/static/js/trial-periods.js"></script>
|
||||
<script src="/static/js/trial-periods-page.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
|
||||
@ -1140,15 +1140,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}).map(c => c.customerName).filter(c => c)
|
||||
).size;
|
||||
|
||||
const completed = new Set(
|
||||
customers.filter(c =>
|
||||
c.statusProgress && (c.statusProgress.includes('已修复') || c.statusProgress.includes('完成') || c.statusProgress.toLowerCase().includes('complete'))
|
||||
).map(c => c.customerName).filter(c => c)
|
||||
).size;
|
||||
|
||||
document.getElementById('totalCustomers').textContent = totalCustomers;
|
||||
document.getElementById('newCustomers').textContent = newCustomers;
|
||||
document.getElementById('completedTasks').textContent = completed;
|
||||
// 已成交数量将在 loadFollowUpCount 中更新
|
||||
}
|
||||
|
||||
// Load follow-up count for dashboard
|
||||
@ -1160,12 +1154,23 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (data.followUps) {
|
||||
const followUpCount = data.followUps.length;
|
||||
document.getElementById('followUpCount').textContent = followUpCount;
|
||||
|
||||
// 计算已成交数量:统计 dealStatus 为"已成交"的唯一客户数
|
||||
const completedCustomers = new Set(
|
||||
data.followUps
|
||||
.filter(f => f.dealStatus === '已成交')
|
||||
.map(f => f.customerName)
|
||||
.filter(name => name)
|
||||
).size;
|
||||
document.getElementById('completedTasks').textContent = completedCustomers;
|
||||
} else {
|
||||
document.getElementById('followUpCount').textContent = '0';
|
||||
document.getElementById('completedTasks').textContent = '0';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading follow-up count:', error);
|
||||
document.getElementById('followUpCount').textContent = '0';
|
||||
document.getElementById('completedTasks').textContent = '0';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1665,14 +1670,45 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
let followupTotalPages = 1;
|
||||
let followupTotalItems = 0;
|
||||
|
||||
// Show/hide follow-up form
|
||||
// 弹窗相关元素
|
||||
const addFollowupModal = document.getElementById('addFollowupModal');
|
||||
const addFollowupModalForm = document.getElementById('addFollowupModalForm');
|
||||
const editFollowupModal = document.getElementById('editFollowupModal');
|
||||
const editFollowupForm = document.getElementById('editFollowupForm');
|
||||
|
||||
// 点击添加跟进按钮 - 弹出弹窗
|
||||
if (addFollowUpBtn) {
|
||||
addFollowUpBtn.addEventListener('click', async function () {
|
||||
followupFormCard.style.display = 'block';
|
||||
await loadCustomerListForFollowup();
|
||||
await loadCustomerListForFollowupModal();
|
||||
addFollowupModal.style.display = 'block';
|
||||
});
|
||||
}
|
||||
|
||||
// 取消添加跟进弹窗
|
||||
if (addFollowupModal) {
|
||||
addFollowupModal.querySelector('.close').addEventListener('click', () => {
|
||||
addFollowupModal.style.display = 'none';
|
||||
addFollowupModalForm.reset();
|
||||
});
|
||||
addFollowupModal.querySelector('.cancel-add-followup').addEventListener('click', () => {
|
||||
addFollowupModal.style.display = 'none';
|
||||
addFollowupModalForm.reset();
|
||||
});
|
||||
}
|
||||
|
||||
// 取消编辑跟进弹窗
|
||||
if (editFollowupModal) {
|
||||
editFollowupModal.querySelector('.close').addEventListener('click', () => {
|
||||
editFollowupModal.style.display = 'none';
|
||||
editFollowupForm.reset();
|
||||
});
|
||||
editFollowupModal.querySelector('.cancel-edit-followup').addEventListener('click', () => {
|
||||
editFollowupModal.style.display = 'none';
|
||||
editFollowupForm.reset();
|
||||
});
|
||||
}
|
||||
|
||||
// 旧表单相关逻辑保留兼容
|
||||
if (cancelFollowupBtn) {
|
||||
cancelFollowupBtn.addEventListener('click', function () {
|
||||
followupFormCard.style.display = 'none';
|
||||
@ -1680,19 +1716,22 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
}
|
||||
|
||||
// Load customer list for follow-up dropdown from trial periods
|
||||
async function loadCustomerListForFollowup() {
|
||||
// Load customer list for follow-up dropdown from trial periods (for modal)
|
||||
async function loadCustomerListForFollowupModal(selectId = 'addFollowupCustomerName') {
|
||||
try {
|
||||
const response = await authenticatedFetch('/api/trial-customers/list');
|
||||
const data = await response.json();
|
||||
|
||||
followupCustomerNameSelect.innerHTML = '<option value="">请选择客户</option>';
|
||||
const selectElement = document.getElementById(selectId);
|
||||
if (!selectElement) return;
|
||||
|
||||
selectElement.innerHTML = '<option value="">请选择客户</option>';
|
||||
if (data.customerNames && data.customerNames.length > 0) {
|
||||
data.customerNames.forEach(customerName => {
|
||||
const option = document.createElement('option');
|
||||
option.value = customerName;
|
||||
option.textContent = customerName;
|
||||
followupCustomerNameSelect.appendChild(option);
|
||||
selectElement.appendChild(option);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@ -1803,6 +1842,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
<td>${formattedTime}</td>
|
||||
<td>${notificationStatus}</td>
|
||||
<td>
|
||||
<button class="action-btn edit-followup-btn" data-id="${followUp.id}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="action-btn delete-followup-btn" data-id="${followUp.id}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
@ -1812,6 +1854,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
followupTableBody.appendChild(row);
|
||||
});
|
||||
|
||||
// Add edit event listeners
|
||||
document.querySelectorAll('.edit-followup-btn').forEach(btn => {
|
||||
btn.addEventListener('click', async function () {
|
||||
const followUpId = this.getAttribute('data-id');
|
||||
await openEditFollowupModal(followUpId);
|
||||
});
|
||||
});
|
||||
|
||||
// Add delete event listeners
|
||||
document.querySelectorAll('.delete-followup-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function () {
|
||||
@ -1844,6 +1894,128 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加跟进弹窗表单提交
|
||||
if (addFollowupModalForm) {
|
||||
addFollowupModalForm.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const followUpTimeValue = document.getElementById('addFollowupTime').value;
|
||||
const followUpTimeISO = new Date(followUpTimeValue).toISOString();
|
||||
|
||||
const customerSelect = document.getElementById('addFollowupCustomerName');
|
||||
const selectedOption = customerSelect.options[customerSelect.selectedIndex];
|
||||
const customerName = selectedOption ? selectedOption.textContent : '';
|
||||
|
||||
const formData = {
|
||||
customerName: customerName,
|
||||
dealStatus: document.getElementById('addFollowupDealStatus').value,
|
||||
customerLevel: document.getElementById('addFollowupCustomerLevel').value,
|
||||
industry: document.getElementById('addFollowupIndustry').value,
|
||||
followUpTime: followUpTimeISO
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await authenticatedFetch('/api/followups', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
addFollowupModalForm.reset();
|
||||
addFollowupModal.style.display = 'none';
|
||||
loadFollowUps();
|
||||
alert('跟进记录创建成功!');
|
||||
} else {
|
||||
alert('创建跟进记录时出错');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating follow-up:', error);
|
||||
alert('创建跟进记录时出错');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 打开编辑跟进弹窗
|
||||
async function openEditFollowupModal(followUpId) {
|
||||
// 从缓存中查找跟进记录
|
||||
const followUp = allFollowUps.find(f => f.id === followUpId);
|
||||
if (!followUp) {
|
||||
alert('未找到跟进记录');
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载客户列表到编辑弹窗
|
||||
await loadCustomerListForFollowupModal('editFollowupCustomerName');
|
||||
|
||||
// 填充表单
|
||||
document.getElementById('editFollowupId').value = followUp.id;
|
||||
document.getElementById('editFollowupCustomerName').value = followUp.customerName || '';
|
||||
document.getElementById('editFollowupDealStatus').value = followUp.dealStatus || '';
|
||||
document.getElementById('editFollowupCustomerLevel').value = followUp.customerLevel || '';
|
||||
document.getElementById('editFollowupIndustry').value = followUp.industry || '';
|
||||
|
||||
// 格式化时间为 datetime-local 格式
|
||||
if (followUp.followUpTime) {
|
||||
const date = new Date(followUp.followUpTime);
|
||||
const localDateTime = date.getFullYear() + '-' +
|
||||
String(date.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(date.getDate()).padStart(2, '0') + 'T' +
|
||||
String(date.getHours()).padStart(2, '0') + ':' +
|
||||
String(date.getMinutes()).padStart(2, '0');
|
||||
document.getElementById('editFollowupTime').value = localDateTime;
|
||||
}
|
||||
|
||||
editFollowupModal.style.display = 'block';
|
||||
}
|
||||
|
||||
// 编辑跟进表单提交
|
||||
if (editFollowupForm) {
|
||||
editFollowupForm.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const followUpId = document.getElementById('editFollowupId').value;
|
||||
const followUpTimeValue = document.getElementById('editFollowupTime').value;
|
||||
const followUpTimeISO = new Date(followUpTimeValue).toISOString();
|
||||
|
||||
const customerSelect = document.getElementById('editFollowupCustomerName');
|
||||
const selectedOption = customerSelect.options[customerSelect.selectedIndex];
|
||||
const customerName = selectedOption ? selectedOption.textContent : '';
|
||||
|
||||
const formData = {
|
||||
customerName: customerName,
|
||||
dealStatus: document.getElementById('editFollowupDealStatus').value,
|
||||
customerLevel: document.getElementById('editFollowupCustomerLevel').value,
|
||||
industry: document.getElementById('editFollowupIndustry').value,
|
||||
followUpTime: followUpTimeISO
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await authenticatedFetch(`/api/followups/${followUpId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
editFollowupForm.reset();
|
||||
editFollowupModal.style.display = 'none';
|
||||
loadFollowUps();
|
||||
alert('跟进记录更新成功!');
|
||||
} else {
|
||||
alert('更新跟进记录时出错');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating follow-up:', error);
|
||||
alert('更新跟进记录时出错');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update follow-up pagination controls
|
||||
function updateFollowupPaginationControls() {
|
||||
const startItem = followupTotalItems === 0 ? 0 : (followupCurrentPage - 1) * followupPageSize + 1;
|
||||
|
||||
@ -296,11 +296,13 @@ function renderTrialPeriodsTable() {
|
||||
// 直接使用 customerName 字段
|
||||
const customerName = period.customerName || '未知客户';
|
||||
|
||||
// 计算状态和到期天数
|
||||
// 计算状态和到期天数(使用日期比较,忽略具体时间)
|
||||
const now = new Date();
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); // 今天 00:00:00
|
||||
const endDate = new Date(period.endTime);
|
||||
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); // 结束日期 00:00:00
|
||||
const startDate = new Date(period.startTime);
|
||||
const daysUntilExpiry = Math.ceil((endDate - now) / (1000 * 60 * 60 * 24));
|
||||
const daysUntilExpiry = Math.round((endDateOnly - today) / (1000 * 60 * 60 * 24));
|
||||
const isTrial = (period.isTrial !== undefined && period.isTrial !== null) ? period.isTrial : false;
|
||||
|
||||
// 生成状态badge
|
||||
@ -308,7 +310,7 @@ function renderTrialPeriodsTable() {
|
||||
if (!isTrial) {
|
||||
statusBadge = '<span class="status-badge status-inactive"><i class="fas fa-pause-circle"></i> 非试用</span>';
|
||||
} else if (daysUntilExpiry < 0) {
|
||||
statusBadge = '<span class="status-badge status-expired"><i class="fas fa-times-circle"></i> 已过期</span>';
|
||||
statusBadge = '<span class="status-badge status-expired"><i class="fas fa-times-circle"></i> 已到期</span>';
|
||||
} else if (daysUntilExpiry === 0) {
|
||||
statusBadge = '<span class="status-badge status-urgent"><i class="fas fa-exclamation-circle"></i> 今日到期</span>';
|
||||
} else if (daysUntilExpiry <= 3) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user