From 4d9da5e3a6b81d936ff069bb0ea81b9cf8b52607 Mon Sep 17 00:00:00 2001 From: chenxing Date: Tue, 6 Oct 2026 20:20:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(appicon):=20AppIcon=20=E8=B5=84=E4=BA=A7?= =?UTF-8?q?=E5=BA=93=E6=8E=A5=E5=85=A5=EF=BC=88=E5=BA=94=E7=94=A8=E7=BD=91?= =?UTF-8?q?=E6=A0=BC=E7=9F=A2=E9=87=8F=E5=9B=BE=E6=A0=87=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=EF=BC=8CYschAppIcons=2010-04=20=E8=BD=AE=20P3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AppIcon.vue:矢量图标渲染组件(iconKey+iconColor → mask data-uri,无键无 URL 回退关键词映射占位) - constants/appIconRules.ts:应用名关键词→图标键兜底映射(D4,两端各持一份,与 MobilePortal 关键词列一致;仅 icon_key 未配置且无 URL 时生效,有 URL 恒定位图零回归) - AppList/SearchPanel/index:应用网格与搜索结果图标位换 AppIcon - appicon.mask.css:资产库 mask 样式;yunhai-logo.svg:站点 Logo 矢量件 - main.scss/_app_container.scss:网格图标容器样式配套 依据:根目录《AppIcon资产库落地-执行报告-20261004.md》P3 UserHome(PC)接入 --- src/assets/image/yunhai-logo.svg | 7 +++ src/assets/styles/appicon.mask.css | 33 ++++++++++++ .../styles/componets/_app_container.scss | 50 +++++++++++++++++++ src/assets/styles/main.scss | 6 +++ src/components/AppIcon.vue | 36 +++++++++++++ src/components/SearchPanel.vue | 16 +++++- src/constants/appIconRules.ts | 43 ++++++++++++++++ src/views/AppList.vue | 24 +++++++-- src/views/index.vue | 18 ++++++- 9 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 src/assets/image/yunhai-logo.svg create mode 100644 src/assets/styles/appicon.mask.css create mode 100644 src/components/AppIcon.vue create mode 100644 src/constants/appIconRules.ts diff --git a/src/assets/image/yunhai-logo.svg b/src/assets/image/yunhai-logo.svg new file mode 100644 index 0000000..7d1d358 --- /dev/null +++ b/src/assets/image/yunhai-logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/styles/appicon.mask.css b/src/assets/styles/appicon.mask.css new file mode 100644 index 0000000..a8db9f3 --- /dev/null +++ b/src/assets/styles/appicon.mask.css @@ -0,0 +1,33 @@ +/* appicon 图标(CSS mask + currentColor,data-uri 自包含),共 2 个。 + 用法 ;命名空间隔离:类名恒为 .appicon*,无 .icon-*。 */ + +.appicon { + font-family: inherit; + font-style: normal; + font-size: 16px; + line-height: 1; + display: inline-block; + vertical-align: middle; +} +.appicon::before { + content: "" !important; + display: block; + width: 1em; + height: 1em; + background-color: currentColor; + -webkit-mask-position: center; + mask-position: center; + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-size: contain; + mask-size: contain; +} + +.appicon-tongzhi::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M853.33 725.33H938.67V810.67H85.33V725.33H170.67V426.67C170.67 238.15 323.49 85.33 512 85.33C700.51 85.33 853.33 238.15 853.33 426.67V725.33ZM768 725.33V426.67C768 285.28 653.38 170.67 512 170.67C370.62 170.67 256 285.28 256 426.67V725.33H768ZM384 896H640V981.33H384V896Z' %3E%3C/path%3E%3C/svg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M853.33 725.33H938.67V810.67H85.33V725.33H170.67V426.67C170.67 238.15 323.49 85.33 512 85.33C700.51 85.33 853.33 238.15 853.33 426.67V725.33ZM768 725.33V426.67C768 285.28 653.38 170.67 512 170.67C370.62 170.67 256 285.28 256 426.67V725.33H768ZM384 896H640V981.33H384V896Z' %3E%3C/path%3E%3C/svg%3E"); +} +.appicon-wenjuan::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M725.33 85.33V170.67H853.61C877.02 170.67 896 189.65 896 213.05V896.28C896 919.69 877.02 938.67 853.61 938.67H170.39C146.98 938.67 128 919.68 128 896.28V213.05C128 189.64 146.98 170.67 170.39 170.67H298.67V85.33H725.33ZM298.67 256H213.33V853.33H810.67V256H725.33V341.33H298.67V256ZM384 682.67V768H298.67V682.67H384ZM384 554.67V640H298.67V554.67H384ZM384 426.67V512H298.67V426.67H384ZM640 170.67H384V256H640V170.67Z' %3E%3C/path%3E%3C/svg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M725.33 85.33V170.67H853.61C877.02 170.67 896 189.65 896 213.05V896.28C896 919.69 877.02 938.67 853.61 938.67H170.39C146.98 938.67 128 919.68 128 896.28V213.05C128 189.64 146.98 170.67 170.39 170.67H298.67V85.33H725.33ZM298.67 256H213.33V853.33H810.67V256H725.33V341.33H298.67V256ZM384 682.67V768H298.67V682.67H384ZM384 554.67V640H298.67V554.67H384ZM384 426.67V512H298.67V426.67H384ZM640 170.67H384V256H640V170.67Z' %3E%3C/path%3E%3C/svg%3E"); +} diff --git a/src/assets/styles/componets/_app_container.scss b/src/assets/styles/componets/_app_container.scss index 55151b8..e172111 100644 --- a/src/assets/styles/componets/_app_container.scss +++ b/src/assets/styles/componets/_app_container.scss @@ -56,3 +56,53 @@ // border-radius: 2px; // background: rgba(255, 255, 255, 0.25); } + +/* ===================== AppIcon 矢量图标三档 scene(方案 §4 渲染契约) ===================== + * 图标资源只管形状,容器管底色与图标色;底色为端侧派生 token(不落库)。 + * 形状类(.appicon-<名> 的 mask data-uri)来自全局 appicon.mask.css; + * AppIcon.vue 组件按 scene/tint 组合类名与内联覆盖(tint=application_info.icon_color 人工配置色)。 + * 尺寸对齐上方 img 位图(60px / 18% 圆角),保证矢量与位图在网格里视觉一致。 + */ +.appicon { + display: inline-block; + line-height: 1; + vertical-align: middle; + box-sizing: border-box; +} + +.appicon-tile { + // PC 应用卡:方块底 + 白图标(对齐位图 60px/18%) + --appicon-bg: var(--brand, #366ef4); // 端侧主题 token(通道 CSS 不可达时兜底蓝) + width: 60px; + height: 60px; + border-radius: 18%; + background: var(--appicon-bg); + color: #fff; + font-size: 44px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.appicon-circle { + // App 首页快捷:正圆底 + 白图标 + --appicon-bg: var(--brand, #366ef4); + width: 50px; + height: 50px; + border-radius: 50%; + background: var(--appicon-bg); + color: #fff; + font-size: 36px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.appicon-plain { + // 无底线性:图标色继承(分组色 / tint) + width: auto; + height: auto; + background: transparent; + color: var(--appicon-ink, currentColor); + font-size: 26px; +} diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss index b9d845a..234433e 100644 --- a/src/assets/styles/main.scss +++ b/src/assets/styles/main.scss @@ -6,6 +6,12 @@ @use "utils/utils"; @use "utils/bsch_title"; +// AppIcon 资产库(CSS mask 版,data-uri 自包含): +// 拷贝自 D:\SoftProject\yunhai\YschAppIcons\appicon.mask.css(资产库 build_mask_css.py 产物)。 +// 同步方式:资产库重跑构建后整文件覆盖本处,避免 P1 静态应用部署顺序依赖与跨域问题。 +// 注意:必须 @use 在 componets/* 之前——scene 档(.appicon-tile 等)需覆盖本文件的 .appicon 基类字号。 +@use "appicon.mask.css"; + @use "componets/status"; @use "componets/small_table"; @use "componets/my_table"; diff --git a/src/components/AppIcon.vue b/src/components/AppIcon.vue new file mode 100644 index 0000000..c99c88c --- /dev/null +++ b/src/components/AppIcon.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/components/SearchPanel.vue b/src/components/SearchPanel.vue index 91895c4..a60abc5 100644 --- a/src/components/SearchPanel.vue +++ b/src/components/SearchPanel.vue @@ -13,7 +13,9 @@
- + +
{{ item.name }}
@@ -26,10 +28,22 @@ import { useDefaultStore } from "@/store"; import { ref, inject, computed, onMounted, watch } from "vue"; import vuescroll from "vuescroll"; +import AppIcon from "@/components/AppIcon.vue"; +import { appiconKeyOf } from "@/constants/appIconRules"; import useCurrentInstance from "@/utils/hook/useCurrentInstance"; const { $utils } = useCurrentInstance(); +/** + * 矢量渲染判定(方案 §5.4 回退链,与 AppList.vue / index.vue 同口径): + * icon_key 命中 → 矢量;有位图 URL → 位图(存量零回归);URL 空且关键词命中 → 矢量;否则默认图。 + */ +function vectorIconOf(item: any): string { + if (item?.iconKey) return item.iconKey; + if (item?.icon) return ""; + return appiconKeyOf(item?.name || ""); +} + const refInput = ref(); const searchName = ref(); diff --git a/src/constants/appIconRules.ts b/src/constants/appIconRules.ts new file mode 100644 index 0000000..67286bb --- /dev/null +++ b/src/constants/appIconRules.ts @@ -0,0 +1,43 @@ +/** + * 应用名称关键词 → AppIcon 资产库图标键映射(D4:两端各持一份,关键词列与 MobilePortal 完全一致)。 + * + * 定位(方案 §5.4 / E4):只做兜底、不做主路径——仅在「icon_key 未配置 且 无 icon URL」时生效 + * (替代默认占位图);有 URL 的存量应用恒定位图渲染,零回归。 + * + * 值列语义:appicon 图标键(如 tongzhi);空串 = 该关键词暂无对应图标(dormant,资产库补图后填值激活)。 + * 一致性断言:YschAppIcons/verify_map.mjs 深比较两端关键词列(含顺序);值列两端允许不同 + * (MobilePortal 用 wd-icon 名,本表用 appicon 键)。 + */ +export const APP_ICON_RULES: [string, string][] = [ + ['请假', ''], + ['课程', ''], + ['课表', ''], + ['通知', ''], + ['公告', ''], + ['报修', ''], + ['文印', ''], + ['打印', ''], + ['公文', ''], + ['作业', ''], + ['班级', ''], + ['家校', ''], + ['会议', ''], + ['场馆', ''], + ['申购', ''], + ['申领', ''], + ['物品', ''], + ['通讯录', ''], + ['邮箱', ''], + ['工资', ''], + ['财务', ''], + ['成绩', ''], +] + +/** 名称命中关键词时返回对应 appicon 图标键;无命中/值为空返回 ''(不渲染矢量,走原回退)。 */ +export function appiconKeyOf(name: string): string { + if (!name) return '' + for (const [kw, key] of APP_ICON_RULES) { + if (name.includes(kw)) return key + } + return '' +} diff --git a/src/views/AppList.vue b/src/views/AppList.vue index fa35116..331d0de 100644 --- a/src/views/AppList.vue +++ b/src/views/AppList.vue @@ -30,7 +30,9 @@ ]" > - + +
{{ citem.name }}
@@ -60,10 +62,10 @@ ]" > - + +
{{ item.name }}
@@ -79,6 +81,7 @@