From 0c4558432aae994ba46e4645a17bf3ed45c00d3d Mon Sep 17 00:00:00 2001 From: zjwmiao <1723168479@qq.com> Date: Sat, 15 Feb 2025 10:11:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0cookie=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 4 +- components.d.ts | 1 + src/App.vue | 16 +- src/components/CookieNotice.vue | 399 ++++++++++++++++++++++++++++++++ src/i18n/cookie/cookie-en.ts | 17 ++ src/i18n/cookie/cookie-zh.ts | 17 ++ src/i18n/cookie/index.ts | 7 + src/i18n/index.ts | 3 + src/main.ts | 4 - src/router/index.ts | 13 +- src/shared/analytics/index.ts | 44 ++++ src/shared/login.ts | 2 - src/stores/common.ts | 36 +++ src/stores/user.ts | 7 +- 14 files changed, 535 insertions(+), 35 deletions(-) create mode 100644 src/components/CookieNotice.vue create mode 100644 src/i18n/cookie/cookie-en.ts create mode 100644 src/i18n/cookie/cookie-zh.ts create mode 100644 src/i18n/cookie/index.ts diff --git a/.env.development b/.env.development index ff3d318..d10989f 100644 --- a/.env.development +++ b/.env.development @@ -1,2 +1,4 @@ VITE_XSRF_COOKIE_NAME = '_U_T_' -VITE_XSRF_HEADER_NAME = 'Token' \ No newline at end of file +VITE_XSRF_HEADER_NAME = 'Token' +VITE_COOKIE_DOMAIN = localhost +VITE_COOKIE_VER = 20240830 diff --git a/components.d.ts b/components.d.ts index db2ada2..72dc207 100644 --- a/components.d.ts +++ b/components.d.ts @@ -19,6 +19,7 @@ declare module 'vue' { AppTableToggle: typeof import('./src/components/AppTableToggle.vue')['default'] CBreadcrumb: typeof import('./src/components/collaboration/CBreadcrumb.vue')['default'] ContentWrapper: typeof import('./src/components/ContentWrapper.vue')['default'] + CookieNotice: typeof import('./src/components/CookieNotice.vue')['default'] DetailAside: typeof import('./src/components/detail/DetailAside.vue')['default'] DetailBasicInfo: typeof import('./src/components/detail/DetailBasicInfo.vue')['default'] DetailHeader: typeof import('./src/components/detail/DetailHeader.vue')['default'] diff --git a/src/App.vue b/src/App.vue index c58b943..5813359 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,7 +5,6 @@ import { RouterView } from 'vue-router'; import { OScroller, OConfigProvider } from '@opensig/opendesign'; import zhCN from '@opensig/opendesign/es/locale/lang/zh-cn'; import enUS from '@opensig/opendesign/es/locale/lang/en-us'; -import { BAIDU_HM } from '@/data/config'; import { useLangStore, useViewStore } from '@/stores/common'; import { useLocale } from '@/composables/useLocale'; @@ -13,11 +12,11 @@ import { useLocale } from '@/composables/useLocale'; import AppHeader from '@/components/header/AppHeader.vue'; import AppFooter from '@/components/AppFooter.vue'; import GlobalFeedback from './components/GlobalFeedback.vue'; +import CookieNotice from './components/CookieNotice.vue'; const langStore = useLangStore(); const viewState = useViewStore(); const { locale } = useI18n(); - watch( () => langStore.lang, (val) => { @@ -27,18 +26,6 @@ watch( // -------------------- 组件国际化 -------------------- const { isZh } = useLocale(); - -// -------------------- 埋点 -------------------- -const initSensor = () => { - // 百度统计 - (function () { - const hm = document.createElement('script'); - hm.src = BAIDU_HM; - const s = document.getElementsByTagName('HEAD')[0]; - s.appendChild(hm); - })(); -}; -initSensor(); diff --git a/src/components/CookieNotice.vue b/src/components/CookieNotice.vue new file mode 100644 index 0000000..9d4ab13 --- /dev/null +++ b/src/components/CookieNotice.vue @@ -0,0 +1,399 @@ + + + + + diff --git a/src/i18n/cookie/cookie-en.ts b/src/i18n/cookie/cookie-en.ts new file mode 100644 index 0000000..716aceb --- /dev/null +++ b/src/i18n/cookie/cookie-en.ts @@ -0,0 +1,17 @@ +export default { + title: 'openEuler Community Respects Your Privacy.', + desc: 'his site uses cookies from us and our partners to improve your browsing experience and make the site work properly. By clicking "Accept All", you consent to the use of cookies. By clicking "Reject All", you disable the use of unnecessary cookies. You can manage your cookie settings by clicking "Manage Cookies". For more information or to change your cookie settings, please refer to our ', + link: 'About Cookies', + acceptAll: 'Accept All', + rejectAll: 'Reject All', + manage: 'Manage Cookies', + necessaryCookie: 'Strictly Necessary Cookies', + necessaryCookieTip: 'Always active', + necessaryCookieDetail: + 'These cookies are necessary for the site to work properly and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as logging in or filling in forms. You can set the browser to block these cookies, but that can make parts of the site not work. These cookies do not store any personally identifiable information.', + analyticalCookie: 'Analytics Cookies', + analyticalCookieDetail: + 'We will use these cookies only with your consent. These cookies help us make improvements by collecting statistics such as the number of visits and traffic sources.', + saveSetting: 'Save and Accept', + allowAll: 'Accept All', +}; diff --git a/src/i18n/cookie/cookie-zh.ts b/src/i18n/cookie/cookie-zh.ts new file mode 100644 index 0000000..d9d1c58 --- /dev/null +++ b/src/i18n/cookie/cookie-zh.ts @@ -0,0 +1,17 @@ +export default { + title: 'openEuler社区重视您的隐私', + desc: '我们在本网站上使用Cookie,包括第三方Cookie,以便网站正常运行和提升浏览体验。单击“全部接受”即表示您同意这些目的;单击“全部拒绝”即表示您拒绝非必要的Cookie;单击“管理Cookie”以选择接受或拒绝某些Cookie。需要了解更多信息或随时更改您的Cookie首选项,请参阅我们的', + link: '《关于cookies》', + acceptAll: '全部接受', + rejectAll: '全部拒绝', + manage: '管理Cookie', + necessaryCookie: '必要Cookie', + necessaryCookieTip: '始终启用', + necessaryCookieDetail: + '这些Cookie是网站正常工作所必需的,不能在我们的系统中关闭。它们通常仅是为了响应您的服务请求而设置的,例如登录或填写表单。您可以将浏览器设置为阻止Cookie来拒绝这些Cookie,但网站的某些部分将无法正常工作。这些Cookie不存储任何个人身份信息。', + analyticalCookie: '统计分析Cookie', + analyticalCookieDetail: + '我们将根据您的同意使用和处理这些非必要Cookie。这些Cookie允许我们获得摘要统计数据,例如,统计访问量和访问者来源,便于我们改进我们的网站。', + saveSetting: '保存并接受', + allowAll: '全部接受', +}; diff --git a/src/i18n/cookie/index.ts b/src/i18n/cookie/index.ts new file mode 100644 index 0000000..a92f0d2 --- /dev/null +++ b/src/i18n/cookie/index.ts @@ -0,0 +1,7 @@ +import zh from './cookie-zh'; +import en from './cookie-en'; + +export default { + zh, + en, +}; diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 3d78515..21b31a3 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -5,6 +5,7 @@ import software from './software'; import upstream from './upstream'; import detail from '@/i18n/detail'; import collaboration from '@/i18n/collaboration'; +import cookie from './cookie'; const messages = { zh: { @@ -13,6 +14,7 @@ const messages = { upstream: upstream.zh, detail: detail.zh, collaboration: collaboration.zh, + cookie: cookie.zh, }, en: { common: common.en, @@ -20,6 +22,7 @@ const messages = { upstream: upstream.en, detail: detail.en, collaboration: collaboration.en, + cookie: cookie.en, }, }; diff --git a/src/main.ts b/src/main.ts index f67547c..6cc0bdf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,13 +19,9 @@ import { initRound } from '@opensig/opendesign'; import App from './App.vue'; import router from './router'; import i18n from './i18n'; -import { enableOA, reportPerformance } from './shared/analytics'; initRound('pill'); -enableOA(); -reportPerformance(); - const app = createApp(App); // 国际化 diff --git a/src/router/index.ts b/src/router/index.ts index fbd5e37..5c10c9b 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -6,7 +6,6 @@ import { tryLogin } from '@/shared/login'; import { useLoginStore, useUserInfoStore } from '@/stores/user'; import { getCollaborationPermissions } from '@/api/api-collaboration'; import { COLLABORATIONPERMISSION } from '@/data/query'; -import { reportPV } from '@/shared/analytics'; const routes = [ { @@ -147,6 +146,7 @@ router.beforeEach(async (to) => { } await tryLogin(); + loginStore.loginStatusChecked = true; // 协作平台权限 if ((userInfoStore.platformMaintainerPermission === null || userInfoStore.platformAdminPermission === null) && loginStore.isLogined) { @@ -172,16 +172,7 @@ router.beforeEach(async (to) => { return true; }); -router.beforeEach((to, from) => { - if (to.name !== 'notFound' && from.path !== '/' && to.path !== from.path) { - to.meta.$referrer = window.location.href; - } -}); - -router.afterEach((to, from) => { - if (to.name !== 'notFound' && to.path !== from.path) { - reportPV(to.meta.$referrer as string); - } +router.afterEach(() => { useViewStore().$patch({ notFoundPage: false }); }); diff --git a/src/shared/analytics/index.ts b/src/shared/analytics/index.ts index 087e3c4..fc1c64b 100644 --- a/src/shared/analytics/index.ts +++ b/src/shared/analytics/index.ts @@ -1,21 +1,62 @@ import { OpenAnalytics, OpenEventKeys, getClientInfo } from '@opensig/open-analytics'; import { reportAnalytics } from '@/api/api-analytics'; import type { Awaitable } from '@vueuse/core'; +import { COOKIE_AGREED_STATUS, useCookieStore } from '@/stores/common'; +import router from '@/router'; export const oa = new OpenAnalytics({ appKey: 'openEuler', request: (data) => { + if (useCookieStore().getUserCookieStatus() !== COOKIE_AGREED_STATUS.ALL_AGREED) { + return disableOA(); + } reportAnalytics(data); }, }); +let reportPerfCount = 0; +let routerGuardCancelFuncs: (() => void)[]; + +const setupRouterGuards = () => { + router.isReady().then(() => { + reportPV(); + (routerGuardCancelFuncs ??= []).push( + router.beforeEach((to, from) => { + if (from.path === '/' || to.path === from.path) { + return; + } + to.meta.$referrer = window.location.href; + }), + router.afterEach((to, from) => { + if (to.path === from.path) { + return; + } + reportPV(to.meta.$referrer as string); + }) + ); + }); +}; + export const enableOA = () => { oa.setHeader(getClientInfo()); oa.enableReporting(true); + setupRouterGuards(); + if (reportPerfCount >= 1) { + return; + } + reportPerfCount++; + reportPerformance(); }; export const disableOA = () => { oa.enableReporting(false); + if (routerGuardCancelFuncs) { + routerGuardCancelFuncs.forEach((item) => item()); + routerGuardCancelFuncs = []; + } + ['oa-openEuler-client', 'oa-openEuler-events', 'oa-openEuler-session'].forEach((key) => { + localStorage.removeItem(key); + }); }; /** @@ -33,6 +74,9 @@ export const oaReport = >( eventOptions?: any; } ) => { + if (!oa.enabled) { + return; + } return oa.report( event, async () => ({ diff --git a/src/shared/login.ts b/src/shared/login.ts index 07c04a4..6158397 100644 --- a/src/shared/login.ts +++ b/src/shared/login.ts @@ -70,7 +70,5 @@ export async function tryLogin() { loginStore.setLoginStatus(LOGIN_STATUS.DONE); } catch (error) { loginStore.setLoginStatus(LOGIN_STATUS.FAILED); - - } } diff --git a/src/stores/common.ts b/src/stores/common.ts index 3d8194f..c5abe61 100644 --- a/src/stores/common.ts +++ b/src/stores/common.ts @@ -1,3 +1,4 @@ +import { getCustomCookie } from '@/utils/cookie'; import { defineStore } from 'pinia'; // 语言 @@ -36,3 +37,38 @@ export const useAppearance = defineStore('appearance', { theme: 'light', }), }); + +export const COOKIE_AGREED_STATUS = { + NOT_SIGNED: '0', // 未签署 + ALL_AGREED: '1', // 同意所有cookie + NECCESSARY_AGREED: '2', // 仅同意必要cookie +}; + +export const COOKIE_KEY = 'agreed-cookiepolicy'; + +export const useCookieStore = defineStore('cookie', { + state: () => ({ + status: COOKIE_AGREED_STATUS.NOT_SIGNED, + version: '20240830', + }), + getters: { + isAllAgreed: (state) => state.status === COOKIE_AGREED_STATUS.ALL_AGREED, + }, + actions: { + getUserCookieStatus() { + const cookieVal = getCustomCookie(COOKIE_KEY) ?? '0'; + const cookieStatusVal = cookieVal[0]; + const privacyVersionVal = cookieVal.slice(1); + if (privacyVersionVal !== this.version) { + this.status = COOKIE_AGREED_STATUS.NOT_SIGNED; + } else if (cookieStatusVal === COOKIE_AGREED_STATUS.ALL_AGREED) { + this.status = COOKIE_AGREED_STATUS.ALL_AGREED; + } else if (cookieStatusVal === COOKIE_AGREED_STATUS.NECCESSARY_AGREED) { + this.status = COOKIE_AGREED_STATUS.NECCESSARY_AGREED; + } else { + this.status = COOKIE_AGREED_STATUS.NOT_SIGNED; + } + return this.status; + }, + }, +}); diff --git a/src/stores/user.ts b/src/stores/user.ts index 499d3c7..666007d 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -16,7 +16,7 @@ export const useUserInfoStore = defineStore('userInfo', { platformAdminPermission: null as boolean | null, // 协作平台maintainer权限 platformMaintainerPermission: null as boolean | null, - } + }; }, getters: { // 获取giteeID @@ -24,7 +24,7 @@ export const useUserInfoStore = defineStore('userInfo', { const id = status.identities.find((id) => id.identity === 'gitee'); return id ? id.login_name : ''; }, - } + }, }); /** @@ -34,6 +34,7 @@ export const useLoginStore = defineStore('login', { state: () => { return { loginStatus: LOGIN_STATUS.NOT, + loginStatusChecked: false, }; }, actions: { @@ -59,4 +60,4 @@ export const useLoginStore = defineStore('login', { return this.loginStatus === LOGIN_STATUS.DONE; }, }, -}); \ No newline at end of file +}); -- Gitee