feat: 内容数据迁移至数据库,合规审查全链路打通
- 文章 HTML 存储从文件系统迁移至 articles 表,删除 releases 目录 - 合规审查从 DB 读取 HTML,审查结果写回 DB,通过后自动推进至待发布 - 新增 todayCount 筛选按钮,与系统概览统计数据一致 - 全屏预览修复:提升 z-index 超过侧边栏,添加退出全屏/关闭按钮 - 统一 '优化' → '审查' 命名,消除前后端术语不一致 - 调度器创作完成后自动触发审查(生成 → 审查 → 待发布) - 清理旧备份/调试文件、过期大纲和研究笔记
This commit is contained in:
@@ -5,29 +5,29 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>宇之然内容创作平台 - 内容日历</title>
|
||||
<link rel="stylesheet" href="element-plus.css">
|
||||
<link rel="stylesheet" href="theme-modern.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background: #f5f7fa; }
|
||||
.navbar { background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); color: white; padding: 16px 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
||||
.navbar-content { display: flex; justify-content: space-between; align-items: center; max-width: 1400px; margin: 0 auto; }
|
||||
.navbar-title { font-size: 20px; font-weight: 600; }
|
||||
.navbar-user { display: flex; align-items: center; gap: 16px; }
|
||||
.avatar { width: 32px; height: 32px; border-radius: 50%; background: rgba(255,255,255,0.2); display: flex; align-items: center; justify-content: center; font-size: 14px; }
|
||||
.main-content { display: flex; flex: 1; max-width: 1400px; margin: 0 auto; }
|
||||
.sidebar { width: 180px; background: white; padding: 12px; box-shadow: 2px 0 8px rgba(0,0,0,0.05); }
|
||||
.content-area { flex: 1; padding: 32px; overflow-y: auto; }
|
||||
.card { background: white; border-radius: 16px; padding: 24px; margin-bottom: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
|
||||
.mobile-nav { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: white; box-shadow: 0 -2px 8px rgba(0,0,0,0.1); padding: 8px 0; z-index: 1000; }
|
||||
.card { background: white; border-radius: 16px; padding: 24px; margin-bottom: 24px; box-shadow: 0 2px 12px rgba(0,0,0,0.06); overflow-x: auto; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sidebar { display: none; }
|
||||
.mobile-nav { display: flex; }
|
||||
.content-area { padding: 12px; padding-bottom: 80px; }
|
||||
.calendar-day { min-height: 56px !important; padding: 2px; }
|
||||
.calendar-weekday { padding: 6px; font-size: 12px; }
|
||||
.day-entry { font-size: 10px; padding: 2px 4px; white-space: normal; }
|
||||
.calendar-grid { gap: 2px; }
|
||||
.day-number { font-size: 12px; margin-bottom: 0; }
|
||||
.day-lunar { font-size: 9px; margin-top: -1px; }
|
||||
.day-term { font-size: 8px; padding: 0 3px; }
|
||||
.day-holiday { font-size: 8px; padding: 0 3px; }
|
||||
}
|
||||
|
||||
.calendar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 12px; }
|
||||
.calendar-title { font-size: 28px; font-weight: 700; color: #303133; }
|
||||
.calendar-nav { display: flex; align-items: center; gap: 16px; }
|
||||
.calendar-nav { display: flex; align-items: center; gap: 16px; white-space: nowrap; }
|
||||
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; }
|
||||
.calendar-weekday { text-align: center; font-weight: 600; color: #606266; padding: 12px; background: #f5f7fa; border-radius: 8px; }
|
||||
.calendar-day { min-height: 100px; background: #fafafa; border-radius: 8px; padding: 8px; border: 1px solid #ebeef5; cursor: pointer; transition: all 0.2s; }
|
||||
@@ -35,6 +35,9 @@
|
||||
.calendar-day.other-month { opacity: 0.4; }
|
||||
.calendar-day.today { border-color: #409EFF; background: #ecf5ff; }
|
||||
.day-number { font-weight: 600; font-size: 14px; margin-bottom: 8px; color: #303133; }
|
||||
.day-lunar { font-size: 10px; color: #909399; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.day-term { display: inline-block; font-size: 9px; color: #E6A23C; background: #fdf6ec; border-radius: 3px; padding: 0 4px; margin-top: 1px; font-weight: 500; white-space: nowrap; }
|
||||
.day-holiday { display: inline-block; font-size: 9px; color: #F56C6C; background: #fef0f0; border-radius: 3px; padding: 0 4px; margin-top: 1px; font-weight: 500; white-space: nowrap; }
|
||||
.day-entries { display: flex; flex-direction: column; gap: 4px; }
|
||||
.day-entry { font-size: 11px; padding: 4px 6px; border-radius: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; }
|
||||
.day-entry.planned { background: #fdf6ec; color: #E6A23C; }
|
||||
@@ -68,7 +71,7 @@
|
||||
></navigation-component>
|
||||
<div class="main-content">
|
||||
<main class="content-area">
|
||||
<div class="card">
|
||||
<div class="card page-fade">
|
||||
<div class="calendar-header">
|
||||
<h2 class="calendar-title">📅 内容日历</h2>
|
||||
<div class="calendar-nav">
|
||||
@@ -93,6 +96,9 @@
|
||||
:class="{ 'other-month': !day.isCurrentMonth, 'today': day.isToday }"
|
||||
@click="openDayDialog(day)">
|
||||
<div class="day-number">{{ day.day }}</div>
|
||||
<div v-if="day.lunar" class="day-lunar">{{ day.lunar }}</div>
|
||||
<div v-if="day.solarTerm" class="day-term">{{ day.solarTerm }}</div>
|
||||
<div v-if="day.holiday" class="day-holiday">{{ day.holiday }}</div>
|
||||
<div class="day-entries">
|
||||
<div v-for="entry in day.entries" :key="entry.id"
|
||||
class="day-entry"
|
||||
@@ -167,6 +173,80 @@
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ---- 农历/节气/节日数据 ----
|
||||
const LUNAR_YEARS = [
|
||||
[2024, 2,10, 0, 30,29,30,29,30,29,30,29,29,30,30,30],
|
||||
[2025, 1,29, 6, 30,29,30,29,30,29,30,29,30,29,30,29,30],
|
||||
[2026, 2,17, 0, 29,30,29,30,29,30,29,30,29,30,29,30],
|
||||
[2027, 2, 6, 0, 30,29,30,29,30,29,30,29,30,29,30,29],
|
||||
[2028, 1,26, 5, 30,29,30,29,30,29,30,30,29,30,29,30,29],
|
||||
];
|
||||
|
||||
const LUNAR_MONTH_NAMES = ['', '正月','二月','三月','四月','五月','六月','七月','八月','九月','十月','冬月','腊月'];
|
||||
const LUNAR_DAY_NAMES = ['', '初一','初二','初三','初四','初五','初六','初七','初八','初九','初十','十一','十二','十三','十四','十五','十六','十七','十八','十九','二十','廿一','廿二','廿三','廿四','廿五','廿六','廿七','廿八','廿九','三十'];
|
||||
|
||||
// 公历节日 (月-日)
|
||||
const SOLAR_HOLIDAYS = {
|
||||
'1-1': '元旦', '2-14': '情人节', '3-8': '妇女节', '3-12': '植树节',
|
||||
'4-1': '愚人节', '5-1': '劳动节', '5-4': '青年节',
|
||||
'6-1': '儿童节', '7-1': '建党节', '8-1': '建军节',
|
||||
'9-10': '教师节', '10-1': '国庆节', '12-25': '圣诞节',
|
||||
};
|
||||
|
||||
// 节气近似日期 (月-日)
|
||||
const SOLAR_TERMS = {
|
||||
'1-5':'小寒','1-20':'大寒','2-4':'立春','2-19':'雨水',
|
||||
'3-6':'惊蛰','3-21':'春分','4-5':'清明','4-20':'谷雨',
|
||||
'5-6':'立夏','5-21':'小满','6-6':'芒种','6-21':'夏至',
|
||||
'7-7':'小暑','7-23':'大暑','8-7':'立秋','8-23':'处暑',
|
||||
'9-7':'白露','9-23':'秋分','10-8':'寒露','10-23':'霜降',
|
||||
'11-7':'立冬','11-22':'小雪','12-7':'大雪','12-22':'冬至',
|
||||
};
|
||||
|
||||
function gregorianToLunar(year, month, day) {
|
||||
const target = new Date(year, month - 1, day).getTime();
|
||||
for (const ly of LUNAR_YEARS) {
|
||||
const [lyear, sm, sd, leap, ...monthDays] = ly;
|
||||
const start = new Date(lyear, sm - 1, sd);
|
||||
const totalDays = monthDays.reduce((a, b) => a + b, 0);
|
||||
const end = new Date(start);
|
||||
end.setDate(end.getDate() + totalDays);
|
||||
if (target < start.getTime() || target >= end.getTime()) continue;
|
||||
const diff = Math.floor((target - start.getTime()) / 86400000);
|
||||
let accum = 0;
|
||||
for (let i = 0; i < monthDays.length; i++) {
|
||||
if (diff < accum + monthDays[i]) {
|
||||
let lunarMonth, isLeap = false;
|
||||
if (leap > 0 && i === leap) {
|
||||
isLeap = true;
|
||||
lunarMonth = leap;
|
||||
} else if (leap > 0 && i > leap) {
|
||||
lunarMonth = i;
|
||||
} else {
|
||||
lunarMonth = i + 1;
|
||||
}
|
||||
const lunarDay = diff - accum + 1;
|
||||
const mName = (isLeap ? '闰' : '') + (LUNAR_MONTH_NAMES[lunarMonth] || lunarMonth + '月');
|
||||
const dName = LUNAR_DAY_NAMES[lunarDay] || lunarDay + '日';
|
||||
let lunarStr = mName + dName;
|
||||
if (lunarStr.length > 6) lunarStr = (isLeap ? '闰' : '') + lunarMonth + '月' + dName;
|
||||
return lunarStr;
|
||||
}
|
||||
accum += monthDays[i];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getDayMeta(year, month, day) {
|
||||
const key = month + '-' + day;
|
||||
const solarTerm = SOLAR_TERMS[key] || '';
|
||||
const holiday = SOLAR_HOLIDAYS[key] || '';
|
||||
const lunar = gregorianToLunar(year, month, day);
|
||||
return { solarTerm, holiday, lunar };
|
||||
}
|
||||
</script>
|
||||
<script src="vue.global.prod.js"></script>
|
||||
<script src="element-plus.full.js"></script>
|
||||
<script>
|
||||
@@ -212,7 +292,8 @@
|
||||
|
||||
for (let i = startWeek - 1; i >= 0; i--) {
|
||||
const d = new Date(currentYear.value, currentMonth.value - 1, -i);
|
||||
days.push({ day: d.getDate(), month: d.getMonth() + 1, year: d.getFullYear(), isCurrentMonth: false, isToday: false, entries: [] });
|
||||
const meta = getDayMeta(d.getFullYear(), d.getMonth() + 1, d.getDate());
|
||||
days.push({ day: d.getDate(), month: d.getMonth() + 1, year: d.getFullYear(), isCurrentMonth: false, isToday: false, entries: [], ...meta });
|
||||
}
|
||||
for (let i = 1; i <= totalDays; i++) {
|
||||
const isToday = today.getFullYear() === currentYear.value && today.getMonth() + 1 === currentMonth.value && today.getDate() === i;
|
||||
@@ -220,12 +301,14 @@
|
||||
const pd = new Date(e.planned_date);
|
||||
return pd.getFullYear() === currentYear.value && pd.getMonth() + 1 === currentMonth.value && pd.getDate() === i;
|
||||
});
|
||||
days.push({ day: i, month: currentMonth.value, year: currentYear.value, isCurrentMonth: true, isToday, entries: dayEntries });
|
||||
const meta = getDayMeta(currentYear.value, currentMonth.value, i);
|
||||
days.push({ day: i, month: currentMonth.value, year: currentYear.value, isCurrentMonth: true, isToday, entries: dayEntries, ...meta });
|
||||
}
|
||||
const remaining = 42 - days.length;
|
||||
for (let i = 1; i <= remaining; i++) {
|
||||
const d = new Date(currentYear.value, currentMonth.value, i);
|
||||
days.push({ day: d.getDate(), month: d.getMonth() + 1, year: d.getFullYear(), isCurrentMonth: false, isToday: false, entries: [] });
|
||||
const meta = getDayMeta(d.getFullYear(), d.getMonth() + 1, d.getDate());
|
||||
days.push({ day: d.getDate(), month: d.getMonth() + 1, year: d.getFullYear(), isCurrentMonth: false, isToday: false, entries: [], ...meta });
|
||||
}
|
||||
return days;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user