This commit is contained in:
@@ -14,7 +14,7 @@ from helpers import add_file_index, attach_common, monthly_finance
|
|||||||
|
|
||||||
bp = Blueprint("api", __name__)
|
bp = Blueprint("api", __name__)
|
||||||
|
|
||||||
ALL_TENANTS = ["总览", "科普·无界", "科研·无界", "医患·无界", "MCN·无界", "学术·无界"]
|
ALL_TENANTS = ["科普·无界", "科研·无界", "医患·无界", "MCN·无界", "学术·无界"]
|
||||||
|
|
||||||
TABLES = {
|
TABLES = {
|
||||||
"sales": ("sales_leads", ["target_customer", "priority", "status", "tenant"]),
|
"sales": ("sales_leads", ["target_customer", "priority", "status", "tenant"]),
|
||||||
@@ -75,7 +75,7 @@ def auth_login():
|
|||||||
session["display_name"] = user["display_name"]
|
session["display_name"] = user["display_name"]
|
||||||
session["role"] = user["role"]
|
session["role"] = user["role"]
|
||||||
if user["role"] == "admin":
|
if user["role"] == "admin":
|
||||||
session["tenants"] = ["总览", "科普·无界", "科研·无界", "医患·无界", "MCN·无界", "学术·无界"]
|
session["tenants"] = ["科普·无界", "科研·无界", "医患·无界", "MCN·无界", "学术·无界"]
|
||||||
else:
|
else:
|
||||||
ut = rows(conn, "SELECT tenant FROM user_tenants WHERE user_id=?", (user["id"],))
|
ut = rows(conn, "SELECT tenant FROM user_tenants WHERE user_id=?", (user["id"],))
|
||||||
session["tenants"] = [x["tenant"] for x in ut]
|
session["tenants"] = [x["tenant"] for x in ut]
|
||||||
@@ -126,10 +126,9 @@ def auth_me():
|
|||||||
if "user_id" not in session:
|
if "user_id" not in session:
|
||||||
return jsonify({"logged_in": False})
|
return jsonify({"logged_in": False})
|
||||||
tenants = session.get("tenants", [])
|
tenants = session.get("tenants", [])
|
||||||
tenants = [t if t != "总工作台" else "总览" for t in tenants]
|
tenants = [t if t != "总工作台" else "科普·无界" for t in tenants]
|
||||||
tenants = [t if t != "学会·无界" else "学术·无界" for t in tenants]
|
tenants = [t if t != "学会·无界" else "学术·无界" for t in tenants]
|
||||||
if "总览" not in tenants:
|
tenants = [t for t in tenants if t != "总览" and t != "总工作台"]
|
||||||
tenants = ["总览"] + tenants
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"logged_in": True,
|
"logged_in": True,
|
||||||
"user": {"id": session["user_id"], "username": session["username"], "display_name": session["display_name"], "role": session["role"]},
|
"user": {"id": session["user_id"], "username": session["username"], "display_name": session["display_name"], "role": session["role"]},
|
||||||
@@ -263,47 +262,13 @@ def bootstrap():
|
|||||||
_year = date.today().year
|
_year = date.today().year
|
||||||
tenant = request.args.get("tenant", session.get("tenants", ["科普·无界"])[0])
|
tenant = request.args.get("tenant", session.get("tenants", ["科普·无界"])[0])
|
||||||
allowed = session.get("tenants", [])
|
allowed = session.get("tenants", [])
|
||||||
allowed = [t if t != "总工作台" else "总览" for t in allowed]
|
allowed = [t if t != "总工作台" else "科普·无界" for t in allowed]
|
||||||
allowed = [t if t != "学会·无界" else "学术·无界" for t in allowed]
|
allowed = [t if t != "学会·无界" else "学术·无界" for t in allowed]
|
||||||
if "总览" not in allowed:
|
allowed = [t for t in allowed if t != "总览" and t != "总工作台"]
|
||||||
allowed = ["总览"] + allowed
|
|
||||||
if tenant not in allowed:
|
if tenant not in allowed:
|
||||||
tenant = allowed[0]
|
tenant = allowed[0] if allowed else ""
|
||||||
conn = db()
|
conn = db()
|
||||||
try:
|
try:
|
||||||
if tenant == "总览":
|
|
||||||
real_tenants = [t for t in allowed if t != "总览"]
|
|
||||||
all_metrics = []
|
|
||||||
all_monthly = []
|
|
||||||
all_recent = []
|
|
||||||
for t in real_tenants:
|
|
||||||
t_ops = attach_common(conn, "operations", rows(conn, "SELECT * FROM operation_projects WHERE tenant=? ORDER BY id ASC", [t]))
|
|
||||||
t_sales = attach_common(conn, "sales", rows(conn, "SELECT * FROM sales_leads WHERE tenant=? ORDER BY id DESC", [t]))
|
|
||||||
t_products = attach_common(conn, "products", rows(conn, "SELECT * FROM product_versions WHERE tenant=? ORDER BY id DESC", [t]))
|
|
||||||
t_proposals = attach_common(conn, "proposals", rows(conn, "SELECT * FROM business_proposals WHERE tenant=? ORDER BY id DESC", [t]))
|
|
||||||
t_pfs = rows(conn, "SELECT * FROM project_finances WHERE tenant=? ORDER BY id DESC", [t])
|
|
||||||
t_expense = rows(conn, "SELECT * FROM expense_records WHERE tenant=? ORDER BY id DESC", [t])
|
|
||||||
t_metrics = compute_metrics(t_pfs, t_ops, t_sales, t_products, t_proposals, t_expense, _year, conn, t)
|
|
||||||
all_metrics.append(t_metrics)
|
|
||||||
all_monthly.append(monthly_finance(conn, t))
|
|
||||||
all_recent.extend(rows(conn, "SELECT * FROM follow_up_records WHERE tenant=? ORDER BY id DESC LIMIT 4", [t]))
|
|
||||||
agg = {}
|
|
||||||
for key in ["total_projects","total_proposals","total_products","upcoming_products","signed_amount","signed_annual","signed_q2","signed_month","signed_prev_q","signed_prev_month","revenue_annual","revenue_q2","monthly_revenue","revenue_prev_q","revenue_prev_month","gross_annual","gross_q2","monthly_net_profit","gross_prev_q","gross_prev_month","payment_annual","payment_q2","payment_month","payment_prev_q","payment_prev_month","cost_annual","cost_q2","cost_month","cost_prev_q","cost_prev_month","expense_annual","expense_q2","expense_month","expense_prev_q","expense_prev_month","paid_annual","paid_q2","paid_month","paid_prev_q","paid_prev_month","cashflow_annual","cashflow_q2","cashflow_month","cashflow_prev_q","cashflow_prev_month","expense_paid_annual","expense_paid_q2","expense_paid_month","expense_paid_prev_q","expense_paid_prev_month","dept_cf_annual","dept_cf_q2","dept_cf_month","dept_cf_prev_q","dept_cf_prev_month","profit_annual","profit_q2","profit_month","profit_prev_q","profit_prev_month","proj_expense_annual","proj_expense_q2","proj_expense_month","proj_expense_prev_q","proj_expense_prev_month"]:
|
|
||||||
agg[key] = sum(m.get(key, 0) for m in all_metrics)
|
|
||||||
merged_monthly = []
|
|
||||||
for i in range(12):
|
|
||||||
m = {"month": all_monthly[0][i]["month"] if all_monthly and len(all_monthly[0]) > i else f"{_year}-{i+1:02d}"}
|
|
||||||
for field in ["revenue","gross","payment","cost","paid","sign"]:
|
|
||||||
m[field] = sum(tl[i][field] if i < len(tl) else 0 for tl in all_monthly)
|
|
||||||
merged_monthly.append(m)
|
|
||||||
summary = {
|
|
||||||
"project_name": "总览",
|
|
||||||
"metrics": agg,
|
|
||||||
"recent": sorted(all_recent, key=lambda x: x.get("id", 0), reverse=True)[:8],
|
|
||||||
"risks": [],
|
|
||||||
}
|
|
||||||
return jsonify({"summary": summary, "financeMonthly": merged_monthly, "tenant": tenant, "tenants": allowed})
|
|
||||||
|
|
||||||
# 普通工作台:只查 summary 所需的数据(project_finances + operations + sales + products + proposals + expense)
|
# 普通工作台:只查 summary 所需的数据(project_finances + operations + sales + products + proposals + expense)
|
||||||
pfs = rows(conn, "SELECT * FROM project_finances WHERE tenant=? ORDER BY id DESC", [tenant])
|
pfs = rows(conn, "SELECT * FROM project_finances WHERE tenant=? ORDER BY id DESC", [tenant])
|
||||||
operations = attach_common(conn, "operations", rows(conn, "SELECT * FROM operation_projects WHERE tenant=? ORDER BY id ASC", [tenant]))
|
operations = attach_common(conn, "operations", rows(conn, "SELECT * FROM operation_projects WHERE tenant=? ORDER BY id ASC", [tenant]))
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ function renderFinance() {
|
|||||||
const finHeaderBase = `<div class="flex items-center gap-2"><button onclick="setFinView('overview')" class="px-3 py-0.5 rounded text-sm font-medium ${state.finView==='overview'?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">总视图</button><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">年份:</span><select onchange="setFinYear(this.value)" class="px-2 py-0.5 rounded text-sm font-medium border border-slate-200 bg-white">${yearOpts.map(y => `<option value="${y}" ${state.finYear===y?'selected':''}>${y}</option>`).join('')}</select><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">季度:</span>${qLabels.map((q,i) => `<button onclick="setFinQuarter(${i})" class="px-3 py-0.5 rounded text-sm font-medium ${state.finView==='quarterly'&&activeQ===i?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">${q}</button>`).join('')}<span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">月度:</span>${activeMonths.map(m => { const ms = `${state.finYear}-${String(m).padStart(2,'0')}`; return `<button onclick="setFinMonth('${ms}')" class="px-3 py-0.5 rounded text-sm font-medium ${state.finView==='monthly'&&state.finMonth===ms?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">${m}月</button>`; }).join('')}</div>`;
|
const finHeaderBase = `<div class="flex items-center gap-2"><button onclick="setFinView('overview')" class="px-3 py-0.5 rounded text-sm font-medium ${state.finView==='overview'?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">总视图</button><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">年份:</span><select onchange="setFinYear(this.value)" class="px-2 py-0.5 rounded text-sm font-medium border border-slate-200 bg-white">${yearOpts.map(y => `<option value="${y}" ${state.finYear===y?'selected':''}>${y}</option>`).join('')}</select><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">季度:</span>${qLabels.map((q,i) => `<button onclick="setFinQuarter(${i})" class="px-3 py-0.5 rounded text-sm font-medium ${state.finView==='quarterly'&&activeQ===i?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">${q}</button>`).join('')}<span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">月度:</span>${activeMonths.map(m => { const ms = `${state.finYear}-${String(m).padStart(2,'0')}`; return `<button onclick="setFinMonth('${ms}')" class="px-3 py-0.5 rounded text-sm font-medium ${state.finView==='monthly'&&state.finMonth===ms?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">${m}月</button>`; }).join('')}</div>`;
|
||||||
const finAddBtn = `<button class="btn btn-primary btn-sm" onclick="openFinanceModal()">新增财务项目</button>`;
|
const finAddBtn = `<button class="btn btn-primary btn-sm" onclick="openFinanceModal()">新增财务项目</button>`;
|
||||||
|
|
||||||
const isOverviewMode = state.tenant === "总览";
|
const isOverviewMode = false;
|
||||||
const finFilterTabs = `<div class="finance-tabs" style="padding:0;border-bottom:1px solid #e2e8f0;margin-bottom:0"><button onclick="switchFinFilter('overview')" class="finance-tab${state.finFilter==='overview'?' active':''}" style="font-size:14px;font-weight:600">经营月报</button><button onclick="switchFinFilter('quarterlyOverview')" class="finance-tab${state.finFilter==='quarterlyOverview'?' active':''}" style="font-size:14px;font-weight:600">经营季报</button>${isOverviewMode ? '' : `<button onclick="switchFinFilter('projects')" class="finance-tab${state.finFilter==='projects'?' active':''}" style="font-size:14px;font-weight:600">项目管理 (${pfs.length})</button><button onclick="switchFinFilter('expense')" class="finance-tab${state.finFilter==='expense'?' active':''}" style="font-size:14px;font-weight:600">平台费用</button>`}</div>`;
|
const finFilterTabs = `<div class="finance-tabs" style="padding:0;border-bottom:1px solid #e2e8f0;margin-bottom:0"><button onclick="switchFinFilter('overview')" class="finance-tab${state.finFilter==='overview'?' active':''}" style="font-size:14px;font-weight:600">经营月报</button><button onclick="switchFinFilter('quarterlyOverview')" class="finance-tab${state.finFilter==='quarterlyOverview'?' active':''}" style="font-size:14px;font-weight:600">经营季报</button>${isOverviewMode ? '' : `<button onclick="switchFinFilter('projects')" class="finance-tab${state.finFilter==='projects'?' active':''}" style="font-size:14px;font-weight:600">项目管理 (${pfs.length})</button><button onclick="switchFinFilter('expense')" class="finance-tab${state.finFilter==='expense'?' active':''}" style="font-size:14px;font-weight:600">平台费用</button>`}</div>`;
|
||||||
|
|
||||||
document.querySelector("#finance").innerHTML = `<div class="grid gap-2">
|
document.querySelector("#finance").innerHTML = `<div class="grid gap-2">
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ function renderHome() {
|
|||||||
}
|
}
|
||||||
document.querySelector("#home").innerHTML = `
|
document.querySelector("#home").innerHTML = `
|
||||||
<div class="grid gap-5">
|
<div class="grid gap-5">
|
||||||
${state.tenant === "总览" ? "" : `<div class="grid grid-cols-4 gap-3">
|
${`<div class="grid grid-cols-4 gap-3">
|
||||||
${[
|
${[
|
||||||
["项目管理", m.total_projects, "finance"],
|
["项目管理", m.total_projects, "finance"],
|
||||||
["重点工作与台账", m.total_proposals, "projects"],
|
["重点工作与台账", m.total_proposals, "projects"],
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ function renderPlan() {
|
|||||||
const finHeaderBase = `<div class="flex items-center gap-2"><button onclick="setPlanView('overview')" class="px-3 py-0.5 rounded text-sm font-medium ${state.planView==='overview'?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">总视图</button><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">年份:</span><select onchange="setPlanYear(this.value)" class="px-2 py-0.5 rounded text-sm font-medium border border-slate-200 bg-white">${yearOpts.map(y => `<option value="${y}" ${state.planYear===y?'selected':''}>${y}</option>`).join('')}</select><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">季度:</span>${qLabels.map((q,i) => `<button onclick="setPlanQuarter(${i})" class="px-3 py-0.5 rounded text-sm font-medium ${state.planView==='quarterly'&&activeQ===i?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">${q}</button>`).join('')}<span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">月度:</span>${activeMonths.map(m => { const ms = `${state.planYear}-${String(m).padStart(2,'0')}`; return `<button onclick="setPlanMonth('${ms}')" class="px-3 py-0.5 rounded text-sm font-medium ${state.planView==='monthly'&&state.planMonth===ms?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">${m}月</button>`; }).join('')}</div>`;
|
const finHeaderBase = `<div class="flex items-center gap-2"><button onclick="setPlanView('overview')" class="px-3 py-0.5 rounded text-sm font-medium ${state.planView==='overview'?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">总视图</button><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">年份:</span><select onchange="setPlanYear(this.value)" class="px-2 py-0.5 rounded text-sm font-medium border border-slate-200 bg-white">${yearOpts.map(y => `<option value="${y}" ${state.planYear===y?'selected':''}>${y}</option>`).join('')}</select><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">季度:</span>${qLabels.map((q,i) => `<button onclick="setPlanQuarter(${i})" class="px-3 py-0.5 rounded text-sm font-medium ${state.planView==='quarterly'&&activeQ===i?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">${q}</button>`).join('')}<span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">月度:</span>${activeMonths.map(m => { const ms = `${state.planYear}-${String(m).padStart(2,'0')}`; return `<button onclick="setPlanMonth('${ms}')" class="px-3 py-0.5 rounded text-sm font-medium ${state.planView==='monthly'&&state.planMonth===ms?'bg-blue-600 text-white':'bg-slate-100 text-slate-600 hover:bg-slate-200'}">${m}月</button>`; }).join('')}</div>`;
|
||||||
const finAddBtn = `<button class="btn btn-primary btn-sm" onclick="openPlanModal()">新增财务项目</button>`;
|
const finAddBtn = `<button class="btn btn-primary btn-sm" onclick="openPlanModal()">新增财务项目</button>`;
|
||||||
|
|
||||||
const isOverviewMode = state.tenant === "总览";
|
const isOverviewMode = false;
|
||||||
const planFilterTabs = `<div class="finance-tabs" style="padding:0 0 0 0;border-bottom:1px solid #e2e8f0;margin-bottom:0;display:flex;gap:4px"><button onclick="switchPlanFilter('overview')" class="finance-tab${state.planFilter==='overview'?' active':''}" style="font-size:14px;font-weight:600;padding:8px 16px;margin-right:0">经营月报</button><button onclick="switchPlanFilter('quarterlyOverview')" class="finance-tab${state.planFilter==='quarterlyOverview'?' active':''}" style="font-size:14px;font-weight:600">经营季报</button>${isOverviewMode ? '' : `<button onclick="switchPlanFilter('projects')" class="finance-tab${state.planFilter==='projects'?' active':''}" style="font-size:14px;font-weight:600">项目管理 (${pfs.length})</button><button onclick="switchPlanFilter('expense')" class="finance-tab${state.planFilter==='expense'?' active':''}" style="font-size:14px;font-weight:600">平台费用计划</button>`}</div>`;
|
const planFilterTabs = `<div class="finance-tabs" style="padding:0 0 0 0;border-bottom:1px solid #e2e8f0;margin-bottom:0;display:flex;gap:4px"><button onclick="switchPlanFilter('overview')" class="finance-tab${state.planFilter==='overview'?' active':''}" style="font-size:14px;font-weight:600;padding:8px 16px;margin-right:0">经营月报</button><button onclick="switchPlanFilter('quarterlyOverview')" class="finance-tab${state.planFilter==='quarterlyOverview'?' active':''}" style="font-size:14px;font-weight:600">经营季报</button>${isOverviewMode ? '' : `<button onclick="switchPlanFilter('projects')" class="finance-tab${state.planFilter==='projects'?' active':''}" style="font-size:14px;font-weight:600">项目管理 (${pfs.length})</button><button onclick="switchPlanFilter('expense')" class="finance-tab${state.planFilter==='expense'?' active':''}" style="font-size:14px;font-weight:600">平台费用计划</button>`}</div>`;
|
||||||
|
|
||||||
document.querySelector("#plan").innerHTML = `<div class="grid gap-2">
|
document.querySelector("#plan").innerHTML = `<div class="grid gap-2">
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ window.toggleTenantMenu = (event) => {
|
|||||||
if (menu) { menu.remove(); return; }
|
if (menu) { menu.remove(); return; }
|
||||||
const btn = event.currentTarget;
|
const btn = event.currentTarget;
|
||||||
const rect = btn.getBoundingClientRect();
|
const rect = btn.getBoundingClientRect();
|
||||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['总览','学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||||
menu = document.createElement("div");
|
menu = document.createElement("div");
|
||||||
menu.id = "tenantMenu";
|
menu.id = "tenantMenu";
|
||||||
menu.className = "fixed bg-white rounded-lg shadow-xl border border-slate-200 py-1 min-w-[160px] z-[9999]";
|
menu.className = "fixed bg-white rounded-lg shadow-xl border border-slate-200 py-1 min-w-[160px] z-[9999]";
|
||||||
@@ -56,7 +56,7 @@ window.switchTenantFromMenu = (tenant) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function updateTenantLabel() {
|
function updateTenantLabel() {
|
||||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['总览','学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||||
var tenantIcons = {
|
var tenantIcons = {
|
||||||
'学术·无界': 'graduation-cap',
|
'学术·无界': 'graduation-cap',
|
||||||
'MCN·无界': 'video',
|
'MCN·无界': 'video',
|
||||||
@@ -84,7 +84,7 @@ window.toggleTenantMenu = (event) => {
|
|||||||
if (menu) { menu.remove(); return; }
|
if (menu) { menu.remove(); return; }
|
||||||
const btn = event.currentTarget;
|
const btn = event.currentTarget;
|
||||||
const rect = btn.getBoundingClientRect();
|
const rect = btn.getBoundingClientRect();
|
||||||
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['总览','学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
const tenants = (state.allowedTenants || []).slice().sort(function(a, b) { var order = ['学术·无界','MCN·无界','科普·无界','医患·无界','科研·无界']; var ai = order.indexOf(a); var bi = order.indexOf(b); if (ai < 0) ai = 99; if (bi < 0) bi = 99; return ai - bi; });
|
||||||
var tenantIcons = {
|
var tenantIcons = {
|
||||||
'学术·无界': 'graduation-cap',
|
'学术·无界': 'graduation-cap',
|
||||||
'MCN·无界': 'video',
|
'MCN·无界': 'video',
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
<header class="topbar border-b border-slate-200 bg-white px-8 py-5">
|
<header class="topbar border-b border-slate-200 bg-white px-8 py-5">
|
||||||
<div class="flex items-center gap-3 w-full">
|
<div class="flex items-center gap-3 w-full">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<p class="eyebrow text-xs font-semibold uppercase tracking-[0.18em] text-blue-700">OPC Manager <span class="ml-2 text-xs font-normal text-slate-400 tracking-normal">v1.2.0.7</span></p>
|
<p class="eyebrow text-xs font-semibold uppercase tracking-[0.18em] text-blue-700">OPC Manager <span class="ml-2 text-xs font-normal text-slate-400 tracking-normal">v1.2.0.8</span></p>
|
||||||
<div class="flex items-center gap-4 mt-1">
|
<div class="flex items-center gap-4 mt-1">
|
||||||
<h1 class="text-2xl font-semibold" id="workspaceTitle">科普 OPC 工作台</h1>
|
<h1 class="text-2xl font-semibold" id="workspaceTitle">科普 OPC 工作台</h1>
|
||||||
<div class="hidden sm:flex items-center gap-1.5 bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-100 rounded-full px-4 py-1.5">
|
<div class="hidden sm:flex items-center gap-1.5 bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-100 rounded-full px-4 py-1.5">
|
||||||
|
|||||||
Reference in New Issue
Block a user