feat: 创作工作台统一 + AI味检测/白标 + 移动端补全 + 合规发布闭环
- 新增创作工作台 studio.html:合并选题/内容工厂/文章管理为单一 tab 入口(iframe embed 模式) - 新增 AI味检测模块(ai_slop API + 页面,合规软硬问题分级) - 新增白标品牌配置(branding API + 页面 + deploy 私有化交付包) - 发布闭环:publishing 放宽至 editor + records/mark-published 接口 - 移动端响应式补全(admin/calendar/ai-slop 表格卡片兜底) - 修复菜单幂等播种缺陷(按 path 对齐,避免功能页孤立) - 新增短视频脚本 shortvideo.py 与 2026 市场调研简报
This commit is contained in:
@@ -107,7 +107,7 @@
|
||||
.uni-nav-dropdown-divider {\
|
||||
height: 1px; background: #f0f0f0; margin: 4px 0;\
|
||||
}\
|
||||
@media (max-width: 768px) {\
|
||||
@media (max-width: 1000px) {\
|
||||
.uni-nav-items { display: none; }\
|
||||
.uni-nav-username { display: none; }\
|
||||
.uni-nav-hamburger { display: inline-flex; }\
|
||||
@@ -121,20 +121,24 @@
|
||||
|
||||
function getCurrentPage() {
|
||||
var path = window.location.pathname;
|
||||
if (path === '/index.html') return 'workspace';
|
||||
var m = path.match(/\/(\w+)\.html/);
|
||||
return m ? m[1] : 'workspace';
|
||||
if (path === '/' || path === '/index.html') return 'index.html';
|
||||
var m = path.match(/\/([\w-]+)\.html$/);
|
||||
return m ? m[1] + '.html' : 'index.html';
|
||||
}
|
||||
|
||||
function navItems(isAdmin) {
|
||||
var items = [
|
||||
{ key: 'workspace', label: '工作台', page: 'index.html' },
|
||||
{ key: 'factory', label: '内容工厂', page: 'factory.html' },
|
||||
{ key: 'insights', label: '数据洞察', page: 'insights.html' },
|
||||
{ key: 'assets', label: '资产库', page: 'assets.html' },
|
||||
{ key: 'dashboard', label: '仪表盘', page: 'index.html' },
|
||||
{ key: 'studio', label: '创作工作台', page: 'studio.html' },
|
||||
{ key: 'insights', label: '数据', page: 'metrics.html' },
|
||||
{ key: 'calendar', label: '日历', page: 'calendar.html' },
|
||||
{ key: 'assets', label: '素材', page: 'assets.html' },
|
||||
{ key: 'tasks', label: '任务', page: 'tasks.html' },
|
||||
{ key: 'campaigns', label: '外推营销', page: 'campaigns.html' },
|
||||
{ key: 'ai-slop', label: 'AI味检测', page: 'ai-slop.html' },
|
||||
];
|
||||
if (isAdmin) {
|
||||
items.push({ key: 'branding', label: '白标设置', page: 'branding.html', admin: true });
|
||||
items.push({ key: 'admin', label: '系统管理', page: 'admin.html', admin: true });
|
||||
}
|
||||
return items;
|
||||
@@ -160,6 +164,9 @@
|
||||
pwdForm: { old_password: '', new_password: '', confirm: '' },
|
||||
pwdSubmitting: false,
|
||||
serverMenus: null,
|
||||
brandName: '',
|
||||
brandLogo: '',
|
||||
primaryColor: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -167,15 +174,19 @@
|
||||
if (this.serverMenus) {
|
||||
return this.serverMenus
|
||||
.filter(function (m) { return m.is_active !== false; })
|
||||
.map(function (m) { return { key: m.name, label: m.name, page: m.path }; });
|
||||
.map(function (m) { return { key: m.name, label: m.name, page: (m.path || '').replace(/^\//, '') }; });
|
||||
}
|
||||
return navItems(this.isAdmin);
|
||||
},
|
||||
page: function () {
|
||||
return this.currentPage || getCurrentPage();
|
||||
return getCurrentPage();
|
||||
},
|
||||
showTitle: function () {
|
||||
return this.title || '宇之然';
|
||||
return this.brandName || this.title || '宇之然';
|
||||
},
|
||||
navStyle: function () {
|
||||
if (!this.primaryColor) return {};
|
||||
return { background: 'linear-gradient(135deg, ' + this.primaryColor + ', ' + this.primaryColor + 'cc)' };
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -189,6 +200,24 @@
|
||||
}.bind(this))
|
||||
.catch(function () {});
|
||||
},
|
||||
fetchBranding: function () {
|
||||
fetch('/api/branding')
|
||||
.then(function (r) { return r.ok ? r.json() : Promise.reject(); })
|
||||
.then(function (d) {
|
||||
if (d.brand_name) this.brandName = d.brand_name;
|
||||
if (d.brand_logo) this.brandLogo = d.brand_logo;
|
||||
if (d.brand_primary_color) {
|
||||
this.primaryColor = d.brand_primary_color;
|
||||
document.documentElement.style.setProperty('--color-primary', d.brand_primary_color);
|
||||
var s = document.getElementById('uni-nav-brand-style');
|
||||
if (!s) { s = document.createElement('style'); s.id = 'uni-nav-brand-style'; document.head.appendChild(s); }
|
||||
s.textContent = '.uni-nav-dropdown-item:hover,.uni-nav-dropdown-item.active{color:' + d.brand_primary_color + ';}' +
|
||||
'.uni-nav-dropdown-item.active{background:' + d.brand_primary_color + '14;}' +
|
||||
'.uni-nav-item.active{background:rgba(255,255,255,0.18);}';
|
||||
}
|
||||
}.bind(this))
|
||||
.catch(function () {});
|
||||
},
|
||||
navigate: function (page) {
|
||||
this.dropdownOpen = false; this.profileOpen = false;
|
||||
if (this.onNavigate) { this.onNavigate(page); return; }
|
||||
@@ -246,18 +275,22 @@
|
||||
mounted: function () {
|
||||
document.addEventListener('click', this.closeAll);
|
||||
this.fetchMenus();
|
||||
this.fetchBranding();
|
||||
},
|
||||
beforeUnmount: function () {
|
||||
document.removeEventListener('click', this.closeAll);
|
||||
},
|
||||
template: '\
|
||||
<nav class="uni-nav" style="position:relative;">\
|
||||
<nav class="uni-nav" :style="navStyle">\
|
||||
<div class="uni-nav-inner">\
|
||||
<div class="uni-nav-brand">{{ showTitle }}</div>\
|
||||
<div class="uni-nav-brand">\
|
||||
<img v-if="brandLogo" :src="brandLogo" style="height:24px;width:auto;margin-right:6px;vertical-align:middle;border-radius:4px;" />\
|
||||
<span>{{ showTitle }}</span>\
|
||||
</div>\
|
||||
<div class="uni-nav-items">\
|
||||
<button v-for="item in items" :key="item.key"\
|
||||
:class="[\'uni-nav-item\', { active: page === item.key }]"\
|
||||
@click="navigate(item.page)">{{ item.label }}</button>\
|
||||
<button v-for="item in items" :key="item.key"\
|
||||
:class="['uni-nav-item', { active: page === item.page }]"\
|
||||
@click="navigate(item.page)">{{ item.label }}</button>\
|
||||
</div>\
|
||||
<div class="uni-nav-right">\
|
||||
<button class="uni-nav-hamburger" @click.stop="toggleDropdown">\
|
||||
@@ -281,7 +314,7 @@
|
||||
</div>\
|
||||
<div :class="[\'uni-nav-dropdown\', { open: dropdownOpen }]" @click.stop>\
|
||||
<button v-for="item in items" :key="item.key"\
|
||||
:class="[\'uni-nav-dropdown-item\', { active: page === item.key }]"\
|
||||
:class="[\'uni-nav-dropdown-item\', { active: page === item.page }]"\
|
||||
@click="navigate(item.page)">\
|
||||
{{ item.label }}\
|
||||
</button>\
|
||||
|
||||
Reference in New Issue
Block a user