dev #1

Merged
kx_guo merged 31 commits from dev into main 2026-09-21 10:50:45 +08:00
11 changed files with 35 additions and 28 deletions
Showing only changes of commit 8ccc9755cb - Show all commits

View File

@ -4,11 +4,10 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#17201d" />
<title>蝶雅风险预测</title>
<title>蝶雅风险预警助手</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -3,7 +3,7 @@ import dd from "dingtalk-jsapi";
import { getDingTalkConfig, loginWithDingTalk } from "@/api/auth";
import type { AuthTokenData } from "@/types/auth";
function isDingTalkContainer() {
export function isDingTalkContainer() {
return (
typeof navigator !== "undefined" && /DingTalk/i.test(navigator.userAgent)
);

View File

@ -95,9 +95,9 @@ const visibleActions = computed(() =>
<template>
<section class="welcome">
<div class="welcome__mark">蝶</div>
<h1>你好,我是风险预估助手 v2.3</h1>
<h1>你好,我是风险预警助手</h1>
<p>
双引擎风险预估:历史异常经验引擎 +
双引擎风险预警:历史异常经验引擎 +
判定标准规则引擎,帮助你在立项、开发和量产阶段提前识别风险。
</p>

View File

@ -50,8 +50,7 @@ function prefetch(item: string) {
<header class="app-header">
<RouterLink class="brand" :to="{ name: 'assessment' }">
<span class="brand__mark">蝶</span>
<span class="brand__name">风险预估助手</span>
<span class="brand__version">v2.3</span>
<span class="brand__name">风险预警助手</span>
<span class="engine-badge">双引擎</span>
</RouterLink>
@ -143,11 +142,6 @@ function prefetch(item: string) {
font-weight: 650;
}
.brand__version {
color: var(--color-text-faint);
font-size: 10px;
}
.engine-badge {
border: 1px solid var(--color-accent-soft);
border-radius: 999px;
@ -252,7 +246,6 @@ nav a.active {
padding-inline: 12px;
}
.brand__version,
.engine-badge,
.top-navigation,
.header-icon,

View File

@ -6,9 +6,10 @@ import {
ShieldCheck,
Smartphone,
} from "@lucide/vue";
import { computed, ref } from "vue";
import { computed, onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { isDingTalkContainer } from "@/api/dingtalk";
import { useAuthStore } from "@/stores/auth.store";
const auth = useAuthStore();
@ -17,6 +18,7 @@ const mode = ref<"password" | "dingtalk">("password");
const useDevelopmentLogin = ref(true);
const username = ref("local-admin");
const password = ref("risk-admin-2026");
const autoDingTalkAttempted = ref(false);
// 登录后的统一落点是智能对话页,避免从其他受保护页面跳转到登录后
// 又回到原页面,保证用户进入系统后先看到对话工作台。
@ -46,6 +48,13 @@ async function submitDingTalk() {
// The store exposes the server or container message below the form.
}
}
onMounted(() => {
if (!isDingTalkContainer() || autoDingTalkAttempted.value) return;
autoDingTalkAttempted.value = true;
mode.value = "dingtalk";
void submitDingTalk();
});
</script>
<template>
@ -54,7 +63,7 @@ async function submitDingTalk() {
<div class="login-card__brand">
<span class="login-card__mark">蝶</span>
<div>
<strong>风险预估助手</strong>
<strong>风险预警助手</strong>
<small>Dieya Risk Prediction Console</small>
</div>
</div>
@ -62,7 +71,7 @@ async function submitDingTalk() {
<div class="login-card__intro">
<span class="eyebrow">SECURE ACCESS</span>
<h1>登录系统</h1>
<p>使用企业账号或钉钉工作台进入风险预估平台。</p>
<p>使用企业账号或钉钉工作台进入风险预警平台。</p>
</div>
<div class="login-tabs" role="tablist" aria-label="登录方式">
@ -126,8 +135,14 @@ async function submitDingTalk() {
<div v-else class="dingtalk-panel">
<div class="dingtalk-panel__icon"><Smartphone :size="22" /></div>
<strong>从钉钉工作台快速进入</strong>
<p>在钉钉容器中点击授权,系统会读取组织身份并自动匹配角色权限。</p>
<strong>{{ auth.loading ? "正在获取钉钉身份" : "从钉钉工作台快速进入" }}</strong>
<p>
{{
auth.loading
? "系统正在验证组织身份并匹配角色权限,请稍候。"
: "系统会读取组织身份并自动匹配角色权限。"
}}
</p>
<button
class="login-submit"
type="button"

View File

@ -130,7 +130,7 @@ onMounted(() => void load());
<header class="dashboard-header">
<div>
<p class="eyebrow">RISK INTELLIGENCE</p>
<h1>数据看板 <small>v2.3</small></h1>
<h1>数据看板</h1>
<p class="dashboard-subtitle">
历史异常、供应商风险与标准覆盖的全局视图
</p>

View File

@ -127,7 +127,7 @@ async function downloadWord() {
const url = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = `蝶雅新品风险预估报告-${report.value.subjectId}.docx`;
anchor.download = `蝶雅新品风险预警报告-${report.value.subjectId}.docx`;
anchor.click();
URL.revokeObjectURL(url);
}
@ -138,7 +138,7 @@ async function downloadPdf() {
const url = URL.createObjectURL(response.data);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = `蝶雅新品风险预估报告-${report.value.subjectId}.pdf`;
anchor.download = `蝶雅新品风险预警报告-${report.value.subjectId}.pdf`;
anchor.click();
URL.revokeObjectURL(url);
}
@ -215,9 +215,9 @@ function dimensionConclusion(item: {
当前会话暂无报告,请返回对话后从风险评估结果卡片点击“生成报告”。
</p>
<article v-else class="report-sheet">
<h1>新品风险预估报告</h1>
<h1>新品风险预警报告</h1>
<p class="report-subtitle">
蝶雅产品部 · 风险预估系统 · 自动生成 · {{ generatedAt }}
蝶雅产品部 · 风险预警系统 · 自动生成 · {{ generatedAt }}
</p>
<div class="report-meta">
<span><b>产品/评估对象:</b>{{ report.subjectId }}</span>
@ -445,7 +445,7 @@ function dimensionConclusion(item: {
</p>
<footer class="report-footer">
<span>编制:蝶雅风险预估系统</span
<span>编制:蝶雅风险预警系统</span
><span>生成时间:{{ generatedAt }}</span>
</footer>
</article>

View File

@ -87,7 +87,7 @@ const permissionRows = [
},
{ role: "审核员", assess: true, submit: false, review: true, weights: false },
{
role: "普通用户",
role: "user",
assess: true,
submit: false,
review: false,

View File

@ -101,7 +101,7 @@ const loadingText = computed(() =>
loading.value ? "正在调用风险评分引擎…" : "",
);
const canMonitor = computed(
() => auth.user?.role === "admin" || auth.user?.role === "manager",
() => auth.user?.role === "admin" || auth.user?.role === "product_manager",
);
onMounted(async () => {

View File

@ -9,7 +9,7 @@ const router = createRouter({
});
router.afterEach((to) => {
document.title = `${String(to.meta.title ?? "风险预测")} | 蝶雅风险预测`;
document.title = `${String(to.meta.title ?? "风险预警助手")} | 蝶雅风险预警助手`;
});
router.beforeEach(async (to) => {

View File

@ -2,7 +2,7 @@ export const roleLabels: Record<string, string> = {
admin: "管理员",
product_manager: "产品经理",
reviewer: "审核员",
普通用户: "普通用户",
user: "普通用户",
};
export function roleLabel(role: string | null | undefined) {