支持钉钉自动免登并统一风险预警品牌
This commit is contained in:
parent
fd26b766a3
commit
8ccc9755cb
@ -4,11 +4,10 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#17201d" />
|
<meta name="theme-color" content="#17201d" />
|
||||||
<title>蝶雅风险预测</title>
|
<title>蝶雅风险预警助手</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import dd from "dingtalk-jsapi";
|
|||||||
import { getDingTalkConfig, loginWithDingTalk } from "@/api/auth";
|
import { getDingTalkConfig, loginWithDingTalk } from "@/api/auth";
|
||||||
import type { AuthTokenData } from "@/types/auth";
|
import type { AuthTokenData } from "@/types/auth";
|
||||||
|
|
||||||
function isDingTalkContainer() {
|
export function isDingTalkContainer() {
|
||||||
return (
|
return (
|
||||||
typeof navigator !== "undefined" && /DingTalk/i.test(navigator.userAgent)
|
typeof navigator !== "undefined" && /DingTalk/i.test(navigator.userAgent)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -95,9 +95,9 @@ const visibleActions = computed(() =>
|
|||||||
<template>
|
<template>
|
||||||
<section class="welcome">
|
<section class="welcome">
|
||||||
<div class="welcome__mark">蝶</div>
|
<div class="welcome__mark">蝶</div>
|
||||||
<h1>你好,我是风险预估助手 v2.3</h1>
|
<h1>你好,我是风险预警助手</h1>
|
||||||
<p>
|
<p>
|
||||||
双引擎风险预估:历史异常经验引擎 +
|
双引擎风险预警:历史异常经验引擎 +
|
||||||
判定标准规则引擎,帮助你在立项、开发和量产阶段提前识别风险。
|
判定标准规则引擎,帮助你在立项、开发和量产阶段提前识别风险。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@ -50,8 +50,7 @@ function prefetch(item: string) {
|
|||||||
<header class="app-header">
|
<header class="app-header">
|
||||||
<RouterLink class="brand" :to="{ name: 'assessment' }">
|
<RouterLink class="brand" :to="{ name: 'assessment' }">
|
||||||
<span class="brand__mark">蝶</span>
|
<span class="brand__mark">蝶</span>
|
||||||
<span class="brand__name">风险预估助手</span>
|
<span class="brand__name">风险预警助手</span>
|
||||||
<span class="brand__version">v2.3</span>
|
|
||||||
<span class="engine-badge">双引擎</span>
|
<span class="engine-badge">双引擎</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
@ -143,11 +142,6 @@ function prefetch(item: string) {
|
|||||||
font-weight: 650;
|
font-weight: 650;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand__version {
|
|
||||||
color: var(--color-text-faint);
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.engine-badge {
|
.engine-badge {
|
||||||
border: 1px solid var(--color-accent-soft);
|
border: 1px solid var(--color-accent-soft);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
@ -252,7 +246,6 @@ nav a.active {
|
|||||||
padding-inline: 12px;
|
padding-inline: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand__version,
|
|
||||||
.engine-badge,
|
.engine-badge,
|
||||||
.top-navigation,
|
.top-navigation,
|
||||||
.header-icon,
|
.header-icon,
|
||||||
|
|||||||
@ -6,9 +6,10 @@ import {
|
|||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
Smartphone,
|
Smartphone,
|
||||||
} from "@lucide/vue";
|
} from "@lucide/vue";
|
||||||
import { computed, ref } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
import { isDingTalkContainer } from "@/api/dingtalk";
|
||||||
import { useAuthStore } from "@/stores/auth.store";
|
import { useAuthStore } from "@/stores/auth.store";
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
@ -17,6 +18,7 @@ const mode = ref<"password" | "dingtalk">("password");
|
|||||||
const useDevelopmentLogin = ref(true);
|
const useDevelopmentLogin = ref(true);
|
||||||
const username = ref("local-admin");
|
const username = ref("local-admin");
|
||||||
const password = ref("risk-admin-2026");
|
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.
|
// 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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -54,7 +63,7 @@ async function submitDingTalk() {
|
|||||||
<div class="login-card__brand">
|
<div class="login-card__brand">
|
||||||
<span class="login-card__mark">蝶</span>
|
<span class="login-card__mark">蝶</span>
|
||||||
<div>
|
<div>
|
||||||
<strong>风险预估助手</strong>
|
<strong>风险预警助手</strong>
|
||||||
<small>Dieya Risk Prediction Console</small>
|
<small>Dieya Risk Prediction Console</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -62,7 +71,7 @@ async function submitDingTalk() {
|
|||||||
<div class="login-card__intro">
|
<div class="login-card__intro">
|
||||||
<span class="eyebrow">SECURE ACCESS</span>
|
<span class="eyebrow">SECURE ACCESS</span>
|
||||||
<h1>登录系统</h1>
|
<h1>登录系统</h1>
|
||||||
<p>使用企业账号或钉钉工作台进入风险预估平台。</p>
|
<p>使用企业账号或钉钉工作台进入风险预警平台。</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-tabs" role="tablist" aria-label="登录方式">
|
<div class="login-tabs" role="tablist" aria-label="登录方式">
|
||||||
@ -126,8 +135,14 @@ async function submitDingTalk() {
|
|||||||
|
|
||||||
<div v-else class="dingtalk-panel">
|
<div v-else class="dingtalk-panel">
|
||||||
<div class="dingtalk-panel__icon"><Smartphone :size="22" /></div>
|
<div class="dingtalk-panel__icon"><Smartphone :size="22" /></div>
|
||||||
<strong>从钉钉工作台快速进入</strong>
|
<strong>{{ auth.loading ? "正在获取钉钉身份" : "从钉钉工作台快速进入" }}</strong>
|
||||||
<p>在钉钉容器中点击授权,系统会读取组织身份并自动匹配角色权限。</p>
|
<p>
|
||||||
|
{{
|
||||||
|
auth.loading
|
||||||
|
? "系统正在验证组织身份并匹配角色权限,请稍候。"
|
||||||
|
: "系统会读取组织身份并自动匹配角色权限。"
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
class="login-submit"
|
class="login-submit"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -130,7 +130,7 @@ onMounted(() => void load());
|
|||||||
<header class="dashboard-header">
|
<header class="dashboard-header">
|
||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">RISK INTELLIGENCE</p>
|
<p class="eyebrow">RISK INTELLIGENCE</p>
|
||||||
<h1>数据看板 <small>v2.3</small></h1>
|
<h1>数据看板</h1>
|
||||||
<p class="dashboard-subtitle">
|
<p class="dashboard-subtitle">
|
||||||
历史异常、供应商风险与标准覆盖的全局视图
|
历史异常、供应商风险与标准覆盖的全局视图
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -127,7 +127,7 @@ async function downloadWord() {
|
|||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const anchor = document.createElement("a");
|
const anchor = document.createElement("a");
|
||||||
anchor.href = url;
|
anchor.href = url;
|
||||||
anchor.download = `蝶雅新品风险预估报告-${report.value.subjectId}.docx`;
|
anchor.download = `蝶雅新品风险预警报告-${report.value.subjectId}.docx`;
|
||||||
anchor.click();
|
anchor.click();
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ async function downloadPdf() {
|
|||||||
const url = URL.createObjectURL(response.data);
|
const url = URL.createObjectURL(response.data);
|
||||||
const anchor = document.createElement("a");
|
const anchor = document.createElement("a");
|
||||||
anchor.href = url;
|
anchor.href = url;
|
||||||
anchor.download = `蝶雅新品风险预估报告-${report.value.subjectId}.pdf`;
|
anchor.download = `蝶雅新品风险预警报告-${report.value.subjectId}.pdf`;
|
||||||
anchor.click();
|
anchor.click();
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
@ -215,9 +215,9 @@ function dimensionConclusion(item: {
|
|||||||
当前会话暂无报告,请返回对话后从风险评估结果卡片点击“生成报告”。
|
当前会话暂无报告,请返回对话后从风险评估结果卡片点击“生成报告”。
|
||||||
</p>
|
</p>
|
||||||
<article v-else class="report-sheet">
|
<article v-else class="report-sheet">
|
||||||
<h1>新品风险预估报告</h1>
|
<h1>新品风险预警报告</h1>
|
||||||
<p class="report-subtitle">
|
<p class="report-subtitle">
|
||||||
蝶雅产品部 · 风险预估系统 · 自动生成 · {{ generatedAt }}
|
蝶雅产品部 · 风险预警系统 · 自动生成 · {{ generatedAt }}
|
||||||
</p>
|
</p>
|
||||||
<div class="report-meta">
|
<div class="report-meta">
|
||||||
<span><b>产品/评估对象:</b>{{ report.subjectId }}</span>
|
<span><b>产品/评估对象:</b>{{ report.subjectId }}</span>
|
||||||
@ -445,7 +445,7 @@ function dimensionConclusion(item: {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<footer class="report-footer">
|
<footer class="report-footer">
|
||||||
<span>编制:蝶雅风险预估系统</span
|
<span>编制:蝶雅风险预警系统</span
|
||||||
><span>生成时间:{{ generatedAt }}</span>
|
><span>生成时间:{{ generatedAt }}</span>
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@ -87,7 +87,7 @@ const permissionRows = [
|
|||||||
},
|
},
|
||||||
{ role: "审核员", assess: true, submit: false, review: true, weights: false },
|
{ role: "审核员", assess: true, submit: false, review: true, weights: false },
|
||||||
{
|
{
|
||||||
role: "普通用户",
|
role: "user",
|
||||||
assess: true,
|
assess: true,
|
||||||
submit: false,
|
submit: false,
|
||||||
review: false,
|
review: false,
|
||||||
|
|||||||
@ -101,7 +101,7 @@ const loadingText = computed(() =>
|
|||||||
loading.value ? "正在调用风险评分引擎…" : "",
|
loading.value ? "正在调用风险评分引擎…" : "",
|
||||||
);
|
);
|
||||||
const canMonitor = computed(
|
const canMonitor = computed(
|
||||||
() => auth.user?.role === "admin" || auth.user?.role === "manager",
|
() => auth.user?.role === "admin" || auth.user?.role === "product_manager",
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const router = createRouter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach((to) => {
|
router.afterEach((to) => {
|
||||||
document.title = `${String(to.meta.title ?? "风险预测")} | 蝶雅风险预测`;
|
document.title = `${String(to.meta.title ?? "风险预警助手")} | 蝶雅风险预警助手`;
|
||||||
});
|
});
|
||||||
|
|
||||||
router.beforeEach(async (to) => {
|
router.beforeEach(async (to) => {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ export const roleLabels: Record<string, string> = {
|
|||||||
admin: "管理员",
|
admin: "管理员",
|
||||||
product_manager: "产品经理",
|
product_manager: "产品经理",
|
||||||
reviewer: "审核员",
|
reviewer: "审核员",
|
||||||
普通用户: "普通用户",
|
user: "普通用户",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function roleLabel(role: string | null | undefined) {
|
export function roleLabel(role: string | null | undefined) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user