Repository navigation
feat: chat theme #7723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: chat theme #7723
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { defineStore } from 'pinia' | |
| import ChatAuthApi from '@/api/chat/auth' | ||
| import type { ApplicationAuthProfileMeta, AuthProfile, ChatLoginRequest } from '@/api/types' | ||
| import { useChatUserStore } from './user' | ||
| import { useChatThemeStore } from './theme' | ||
|
|
||
| /** 门户的认证场景;accessToken 为 16 位十六进制字符串,不会与之重名。 */ | ||
| export const PORTAL_AUTH_SCOPE = 'portal' | ||
|
|
@@ -81,11 +82,19 @@ export const useChatAuthStore = defineStore('chat-auth', { | |
| return token === this.expiredToken ? '' : token | ||
| }, | ||
|
|
||
| /** 获取指定场景的认证前置配置;与当前场景相同时复用。 */ | ||
| /** 获取并应用指定场景的认证前置主题;与当前场景相同时复用配置。 */ | ||
| fetchAuthProfile(scope: string): Promise<AuthProfile<unknown>> { | ||
| if (scope === this.scope && this.authProfile) return Promise.resolve(this.authProfile) | ||
|
|
||
| return isPortalScope(scope) ? ChatAuthApi.getPortalAuthProfile() : ChatAuthApi.getApplicationAuthProfile(scope) | ||
| const profileRequest = | ||
| scope === this.scope && this.authProfile | ||
| ? Promise.resolve(this.authProfile) | ||
| : isPortalScope(scope) | ||
| ? ChatAuthApi.getPortalAuthProfile() | ||
| : ChatAuthApi.getApplicationAuthProfile(scope) | ||
|
|
||
| return profileRequest.then((authProfile) => { | ||
| useChatThemeStore().setTheme(authProfile.custom_theme?.theme_color) | ||
| return authProfile | ||
| }) | ||
| }, | ||
|
|
||
| /** 切换当前场景,由守卫在放行前调用。 */ | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Main concern: Potential issues
RecommendationKeep |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| /** 管理 Chat 入口的智能体主题色,并按需应用到当前文档。 */ | ||
|
|
||
| import { defineStore } from 'pinia' | ||
| import { DEFAULT_THEME_COLOR } from '@/constants' | ||
|
|
||
| export const useChatThemeStore = defineStore('chat-theme', { | ||
| state: () => ({ themeColor: DEFAULT_THEME_COLOR }), | ||
|
|
||
| actions: { | ||
| /** 配置缺失或非法时使用默认色;主题只保存在内存中。 */ | ||
| setTheme(themeColor?: string) { | ||
| const normalizedColor = themeColor?.trim().toLowerCase() | ||
| this.themeColor = normalizedColor && /^#[\da-f]{6}$/.test(normalizedColor) ? normalizedColor : DEFAULT_THEME_COLOR | ||
|
|
||
| const rootStyle = document.documentElement.style | ||
| const hexColor = this.themeColor.slice(1) | ||
| const rgbChannels = [0, 2, 4].map((index) => Number.parseInt(hexColor.slice(index, index + 2), 16)).join(' ') | ||
| rootStyle.setProperty('--mk-primary', this.themeColor) | ||
| rootStyle.setProperty('--mk-primary-rgb', rgbChannels) | ||
| if (this.themeColor === DEFAULT_THEME_COLOR) { | ||
| rootStyle.removeProperty('--mk-primary-gradient') | ||
| rootStyle.removeProperty('--mk-primary-gradient-end') | ||
| } else { | ||
| rootStyle.setProperty('--mk-primary-gradient', this.themeColor) | ||
| rootStyle.setProperty('--mk-primary-gradient-end', this.themeColor) | ||
| } | ||
| }, | ||
|
|
||
| /** 恢复默认主题色与默认蓝紫渐变。 */ | ||
| resetTheme() { | ||
| this.setTheme() | ||
| }, | ||
| }, | ||
| }) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overall, the code is functionally reasonable, but there are a few edge cases and architectural concerns worth addressing. Potential issues
Optimization suggestions
Example refactor sketchconst HEX_COLOR_RE = /^#[0-9a-f]{6}$/
const DEFAULT_COLOR = DEFAULT_THEME_COLOR.toLowerCase()
function normalizeColor(color?: string): string {
const normalized = color?.trim().toLowerCase()
return normalized && HEX_COLOR_RE.test(normalized) ? normalized : DEFAULT_COLOR
}
function hexToRgbChannels(hex: string): string {
const value = hex.slice(1)
const r = Number.parseInt(value.slice(0, 2), 16)
const g = Number.parseInt(value.slice(2, 4), 16)
const b = Number.parseInt(value.slice(4, 6), 16)
return `${r} ${g} ${b}`
}
export const useChatThemeStore = defineStore('chat-theme', {
state: () => ({ themeColor: DEFAULT_COLOR }),
actions: {
setTheme(themeColor?: string) {
const color = normalizeColor(themeColor)
this.themeColor = color
if (typeof document === 'undefined') return
const rootStyle = document.documentElement.style
rootStyle.setProperty('--mk-primary', color)
rootStyle.setProperty('--mk-primary-rgb', hexToRgbChannels(color))
if (color === DEFAULT_COLOR) {
rootStyle.removeProperty('--mk-primary-gradient')
rootStyle.removeProperty('--mk-primary-gradient-end')
} else {
rootStyle.setProperty('--mk-primary-gradient', color)
rootStyle.setProperty('--mk-primary-gradient-end', color)
}
},
resetTheme() {
this.setTheme(DEFAULT_COLOR)
},
},
})No major functional bug is present if |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, the diff is mostly formatting/import-style cleanup and does not change the main logic. However, there are a few points worth checking:
Potential issues
border-N300was removedborder-N300was a valid design token, this is a visual regression: the border color will change to the default/inherited color.border-N300was invalid or no longer used, this is fine, but prefer using a valid token such asborder-neutral-300or the project’s actual token.border-N300unless the color change is intentional.Long template lines may conflict with the formatter/linter
vue/max-attributes-per-line, this may fail formatting/lint checks.Duplicate readonly condition
is_submitandviewTypeare refs:Then:
Duplicate
editorIdon twoMdPreviewcomponentseditorIdis used as a DOM id or editor instance key, using it twice may cause conflicts.submitshould also guard against readonly/duplicate submissionsubmitis still useful:submitperforms an async request, ensure it cannot be triggered twice while pending. Set a submitting flag beforeawaitand reset it infinally.Minor suggestions
"to'is fine and likely consistent with project style.</script>is harmless.is_submit,form_data, andform_field_listare inconsistent with typical TS/Vue camelCase style, but this is only a style issue if the project enforces it.READONLY_VIEWS.includes(...)is fine for two items. If the list grows significantly, consider aSet<ViewType>:Most important action
Check whether removing
border-N300is intentional. If not, restore it. Then run the project formatter and consider extracting the repeated readonly condition into a computed property.