feat: 内容数据迁移至数据库,合规审查全链路打通

- 文章 HTML 存储从文件系统迁移至 articles 表,删除 releases 目录
- 合规审查从 DB 读取 HTML,审查结果写回 DB,通过后自动推进至待发布
- 新增 todayCount 筛选按钮,与系统概览统计数据一致
- 全屏预览修复:提升 z-index 超过侧边栏,添加退出全屏/关闭按钮
- 统一 '优化' → '审查' 命名,消除前后端术语不一致
- 调度器创作完成后自动触发审查(生成 → 审查 → 待发布)
- 清理旧备份/调试文件、过期大纲和研究笔记
This commit is contained in:
Yuzhiran Dev
2026-05-13 17:33:56 +08:00
parent bc6a302e59
commit 233e23016c
234 changed files with 5670 additions and 10651 deletions
+98 -15
View File
@@ -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;
});