npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@huatu-lcy/system-notification

v0.1.1

Published

Reusable Vue 2 SSE system notification component.

Downloads

20

Readme

SystemNotification

SystemNotification 是一个基于 SSE EventSource 的系统通知接入组件。组件本身不渲染可见入口,挂载后自动建立 SSE 连接,收到业务消息后通过 notify 或 Vue 实例上的 $notify 展示当前缓存的所有消息。

该目录已经把组件、SSE 连接工具、消息格式化工具集中导出,方便后续作为 npm 组件发布给其他系统复用。

npm 包构建

组件目录已经包含独立 npm 包配置,包名为 @huatu-lcy/system-notification。发布前如需使用公司 npm scope 或私有 registry,可先修改 package.json 中的 name、version、license 等字段。

# 从仓库根目录构建组件库产物
pnpm run build:system-notification

# 从仓库根目录生成本地 npm tarball
pnpm run pack:system-notification

# 确认 npm 登录态、registry、包名和版本后再发布
pnpm --dir src/common/components/SystemNotification publish --registry <npm-registry-url>

构建产物会生成到 src/common/components/SystemNotification/dist,包含 ESM、CJS、UMD 和样式文件。接入项目应自行安装 vue@2;如果使用默认 $notify,还需要安装并注册 element-ui@2。它们作为 peer dependency 不会被打包进组件库。

完整发布和系统接入流程见 NPM_PUBLISH_AND_USAGE.md。

基本用法

npm 包使用

pnpm add @huatu-lcy/system-notification
import Vue from 'vue'
import ElementUI from 'element-ui'
import SystemNotification from '@huatu-lcy/system-notification'
import '@huatu-lcy/system-notification/style.css'

Vue.use(ElementUI)
Vue.use(SystemNotification)
<template>
    <system-notification
        user-id="10001"
        sse-url="https://notice.example.com/stream/connect"
        token="access-token"
        group="crm"
        :event-names="['group-message', 'private-message', 'broadcast-message']"
    />
</template>

项目内直接使用

<template>
    <system-notification
        :user-id="userInfo.staffNo"
        sse-url="/api/sse/connect"
        token="access-token"
        :event-names="['group-message', 'private-message', 'broadcast-message']"
    />
</template>

<script>
import SystemNotification from '@/common/components/SystemNotification/index.js'

export default {
    components: {
        SystemNotification
    },
    computed: {
        userInfo() {
            return this.$store.state.userInfo || {}
        }
    }
}
</script>

作为插件注册

import Vue from 'vue'
import ElementUI from 'element-ui'
import SystemNotification from '@/common/components/SystemNotification/index.js'

Vue.use(ElementUI)
Vue.use(SystemNotification)
<template>
    <system-notification
        user-id="10001"
        sse-url="/api/sse/connect"
        token="access-token"
        group="crm"
        :event-names="['group-message', 'private-message', 'broadcast-message']"
    />
</template>

自定义连接地址和必要参数

<template>
    <system-notification
        sse-url="https://notice.example.com/stream/connect"
        token-key="accessToken"
        user-id-key="staffNo"
        group-key="tenant"
        token="access-token"
        user-id="10001"
        group="tenant-a"
        :event-names="['tenant-message', 'private-message']"
        :connection-params="{
            appId: 'crm',
            locale: 'zh-CN'
        }"
        :required-params="['userId', 'appId']"
    />
</template>

上面的配置会连接到:

https://notice.example.com/stream/connect?accessToken=access-token&staffNo=10001&tenant=tenant-a&appId=crm&locale=zh-CN

自定义监听事件

<template>
    <system-notification
        user-id="10001"
        sse-url="https://notice.example.com/stream/connect"
        token="access-token"
        :event-names="['tenant-message', 'private-message']"
        connected-event-name="ready"
        heartbeat-event-name="ping"
        :event-handlers="eventHandlers"
        @notification="handleNotification"
        @error="handleError"
    />
</template>

<script>
export default {
    data() {
        return {
            eventHandlers: {
                'custom-event': this.handleCustomEvent
            }
        }
    },
    methods: {
        handleNotification(notification, messages, event) {
            console.log(notification, messages, event)
        },
        handleCustomEvent(payload, event) {
            console.log(payload, event)
        },
        handleError(error) {
            console.error(error)
        }
    }
}
</script>

自定义通知展示

<template>
    <system-notification
        user-id="10001"
        sse-url="/api/sse/connect"
        token="access-token"
        :event-names="['group-message', 'private-message', 'broadcast-message']"
        :notify-options="{
            title: '待办提醒',
            position: 'bottom-right',
            duration: 5000
        }"
        :notification-formatter="formatNotification"
    />
</template>

<script>
export default {
    methods: {
        formatNotification(messages, defaultOptions) {
            return {
                ...defaultOptions,
                message: messages.map(item => item.content).join('<br />')
            }
        }
    }
}
</script>

使用插槽自定义通知内容

当调用方需要完全控制 $notify 内部展示时,可以使用 scoped slot。传入插槽后组件会把 $notify 的 message 改为 VNode,并关闭 dangerouslyUseHTMLString。

<template>
    <system-notification
        user-id="10001"
        sse-url="/api/sse/connect"
        token="access-token"
        :event-names="['group-message', 'private-message', 'broadcast-message']"
        title="待办提醒"
    >
        <template #content="{ messages, latestMessage, close }">
            <div class="todo-notice">
                <div class="todo-notice__header">
                    <span>最新消息:{{ latestMessage && latestMessage.content }}</span>
                    <button type="button" @click="close">关闭</button>
                </div>
                <div
                    v-for="message in messages"
                    :key="message.id || message.sendTime"
                    class="todo-notice__item"
                >
                    <strong>{{ message.type }}</strong>
                    <p>{{ message.content }}</p>
                </div>
            </div>
        </template>
    </system-notification>
</template>

只想替换单条消息的展示时,使用 item 插槽:

<template>
    <system-notification
        user-id="10001"
        sse-url="/api/sse/connect"
        token="access-token"
        :event-names="['group-message', 'private-message', 'broadcast-message']"
    >
        <template #item="{ message, index }">
            <div class="custom-notice-item">
                <span>{{ index + 1 }}.</span>
                <span>{{ message.fromUserId }}:{{ message.content }}</span>
            </div>
        </template>
        <template #empty>
            <span>暂无待处理消息</span>
        </template>
    </system-notification>
</template>

如果接入项目不使用 Element UI,可以通过 notify 注入自己的通知函数:

<template>
    <system-notification
        user-id="10001"
        sse-url="/api/sse/connect"
        token="access-token"
        :event-names="['group-message', 'private-message', 'broadcast-message']"
        :notify="notify"
    />
</template>

<script>
export default {
    methods: {
        notify(options) {
            const close = window.CustomNotice.open(options)
            return {
                close
            }
        }
    }
}
</script>

完整配置示例(所有 props)

下面示例刻意展开所有 props,实际接入时可以删除不需要覆盖默认值的配置。若要使用 slot 自定义 $notify 内容,notificationFormatter 请保持 null;如果传入 formatter,它会优先接管通知内容。

<template>
    <system-notification
        :user-id="notificationProps.userId"
        :token="notificationProps.token"
        :group="notificationProps.group"
        :sse-url="notificationProps.sseUrl"
        :token-key="notificationProps.tokenKey"
        :user-id-key="notificationProps.userIdKey"
        :group-key="notificationProps.groupKey"
        :connection-params="notificationProps.connectionParams"
        :required-params="notificationProps.requiredParams"
        :event-source-options="notificationProps.eventSourceOptions"
        :event-names="notificationProps.eventNames"
        :connected-event-name="notificationProps.connectedEventName"
        :heartbeat-event-name="notificationProps.heartbeatEventName"
        :event-handlers="notificationProps.eventHandlers"
        :title="notificationProps.title"
        :max-messages="notificationProps.maxMessages"
        :disabled="notificationProps.disabled"
        :notify="showSystemNotify"
        :notify-options="notificationProps.notifyOptions"
        :notification-formatter="notificationProps.notificationFormatter"
        :normalizer="normalizeNotificationPayload"
        :sse-factory="createSseClient"
        :on-open="handleOpen"
        :on-connected="handleConnected"
        :on-heartbeat="handleHeartbeat"
        :on-message="handleMessage"
        :on-notification="handleNotification"
        :on-error="handleError"
        :on-notify="handleNotify"
    >
        <template #content="{ messages, latestMessage, connected, close }">
            <div class="custom-system-notification">
                <div class="custom-system-notification__header">
                    <span>{{ connected ? '已连接' : '未连接' }}</span>
                    <strong>{{ latestMessage ? latestMessage.content : '暂无系统通知' }}</strong>
                    <button type="button" @click="close">关闭</button>
                </div>
                <div
                    v-for="message in messages"
                    :key="message.id || message.sendTime || message.timestamp"
                    class="custom-system-notification__item"
                >
                    <div>{{ message.type || message.code || '系统消息' }}</div>
                    <div>{{ message.fromUserId || '--' }} -> {{ message.toUserId || '全部用户' }}</div>
                    <p>{{ message.content || message.message || '--' }}</p>
                </div>
            </div>
        </template>
    </system-notification>
</template>

<script>
import SystemNotification, {
    createNotificationSse,
    normalizeNotification
} from '@/common/components/SystemNotification/index.js'

export default {
    components: {
        SystemNotification
    },
    data() {
        return {
            notificationProps: {
                userId: '10001',
                token: 'access-token',
                group: 'tenant-a',
                sseUrl: 'https://notice.example.com/stream/connect',
                tokenKey: 'accessToken',
                userIdKey: 'staffNo',
                groupKey: 'tenant',
                connectionParams: {
                    appId: 'crm',
                    tenantId: 'tenant-a',
                    locale: 'zh-CN'
                },
                requiredParams: ['userId', 'appId', 'tenantId'],
                eventSourceOptions: {
                    withCredentials: true
                },
                eventNames: ['tenant-message', 'private-message', 'broadcast-message'],
                connectedEventName: 'ready',
                heartbeatEventName: 'ping',
                eventHandlers: {},
                title: '系统通知',
                maxMessages: 30,
                disabled: false,
                notifyOptions: {
                    position: 'bottom-right',
                    duration: 5000,
                    showClose: true,
                    customClass: 'system-notification-notify'
                },
                notificationFormatter: null
            }
        }
    },
    created() {
        this.notificationProps.eventHandlers = {
            'custom-event': this.handleCustomEvent
        }
    },
    methods: {
        showSystemNotify(options) {
            return this.$notify(options)
        },
        normalizeNotificationPayload(payload) {
            return normalizeNotification(payload)
        },
        createSseClient(options) {
            return createNotificationSse(options)
        },
        handleOpen(payload, event) {
            console.log('open', payload, event)
        },
        handleConnected(payload, event) {
            console.log('connected', payload, event)
        },
        handleHeartbeat(payload, event) {
            console.log('heartbeat', payload, event)
        },
        handleMessage(payload, event) {
            console.log('raw message', payload, event)
        },
        handleNotification(notification, messages, event) {
            console.log('notification', notification, messages, event)
        },
        handleError(error) {
            console.error(error)
        },
        handleNotify({ messages, options, instance }) {
            console.log('notify shown', messages, options, instance)
        },
        handleCustomEvent(payload, event) {
            console.log('custom event', payload, event)
        }
    }
}
</script>

Attributes

| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | userId | String \| Number | '' | 当前接收通知的用户标识,默认作为 userId query 参数发送。 | | token | String | 必传 | SSE 服务鉴权 token,默认作为 token query 参数发送。公共组件不内置默认 token。 | | group | String | '' | 分组或租户标识,默认作为 group query 参数发送。需要分组时由调用方传入。 | | sseUrl | String | 必传 | SSE 完整连接地址,例如 /api/sse/connect 或 https://notice.example.com/stream/connect。公共组件不再拼接 origin/path。 | | tokenKey | String | 'token' | token 在 query 中的参数名。传空字符串可不发送 token query,但 token prop 仍是必传字段。 | | userIdKey | String | 'userId' | userId 在 query 中的参数名。传空字符串可不发送 userId。 | | groupKey | String | 'group' | group 在 query 中的参数名。传空字符串可不发送 group。 | | connectionParams | Object | {} | 额外 query 参数,例如 appId、tenantId、locale。同名字段会覆盖默认 query 参数。 | | requiredParams | Array<String> | ['userId'] | 建立连接前必须存在的额外参数名。sseUrl 和 token 固定必填;该配置支持 userId、group,以及 connectionParams 中的字段。 | | eventSourceOptions | Object | {} | 传给 new EventSource(url, options) 的配置。默认 { withCredentials: false },可覆盖为 { withCredentials: true }。 | | eventNames | Array<String> | 必传 | 业务通知事件名列表,这些事件会触发通知入队和展示。公共组件不内置业务事件名;未监听到的命名事件不会进入通知列表。 | | connectedEventName | String | 'connected' | 连接成功事件名。传空字符串可关闭该事件监听。 | | heartbeatEventName | String | 'heartbeat' | 心跳事件名。传空字符串可关闭该事件监听。 | | eventHandlers | Object | {} | 额外事件处理器,格式为 { eventName: handler }。handler 参数为 (payload, event)。 | | title | String | '系统通知' | 默认 $notify 标题。 | | maxMessages | Number | 20 | 本地缓存并展示的最大消息数量。 | | disabled | Boolean | false | 是否禁用连接。切为 true 时会关闭 SSE 连接和当前通知实例。 | | notify | Function | null | 自定义通知函数。未传时使用组件实例上的 $notify。函数参数为通知 options,返回值可包含 close()。 | | notifyOptions | Object | {} | 合并进默认 $notify options 的配置。 | | notificationFormatter | Function | null | 自定义通知内容格式化函数,参数为 (messages, defaultOptions)。可返回字符串或完整 notify options。 | | normalizer | Function | normalizeNotification | 消息标准化函数,参数为原始 payload,返回标准消息对象。返回空值时忽略该消息。 | | sseFactory | Function | createNotificationSse | 自定义 SSE 客户端工厂,适合单测或替换通信实现。 | | onOpen | Function | null | open 回调,参数为 (payload, event)。 | | onConnected | Function | null | connected 回调,参数为 (payload, event)。 | | onHeartbeat | Function | null | heartbeat 回调,参数为 (payload, event)。 | | onMessage | Function | null | 原始 onmessage 回调,参数为 (payload, event)。 | | onNotification | Function | null | 标准化后的业务通知回调,参数为 (notification, messages, event)。 | | onError | Function | null | 错误回调,参数为 (error)。 | | onNotify | Function | null | 通知展示后的回调,参数为 ({ messages, options, instance })。 |

Events

组件会同时支持 Vue 事件和 onXxx 函数式回调。Vue 事件适合模板中监听,onXxx 更适合 npm 组件在不同项目中做配置化接入。

| 事件名 | 参数 | 说明 | | --- | --- | --- | | open | (payload, event) | SSE 连接打开时触发。 | | connected | (payload, event) | 收到 connectedEventName 对应事件时触发。 | | heartbeat | (payload, event) | 收到 heartbeatEventName 对应事件时触发。 | | message | (notification, messages) | 收到业务消息并完成标准化后触发。 | | notification | (notification, messages, event) | 收到业务消息并完成标准化后触发,比 message 多透出原始 SSE event。 | | error | (error) | SSE 连接报错或浏览器不支持 EventSource 时触发。 | | notify | ({ messages, options }) | 调用通知函数后触发。 |

Slots

组件本体仍是无界面的连接型组件,slot 不会直接渲染在页面中,只会作为 $notify 的消息内容渲染。

| 插槽名 | 参数 | 说明 | | --- | --- | --- | | content | { messages, latestMessage, title, connected, close, options } | 自定义整块 $notify 内容。存在该插槽时优先使用它,不再渲染默认列表和 item 插槽。 | | item | { message, index, messages, latestMessage, title, connected, close, options } | 自定义每条消息内容,外层列表、空状态和单条消息容器仍由组件提供。 | | empty | { messages, latestMessage, title, connected, close, options } | 自定义空消息展示。 |

插槽参数说明:

| 参数 | 类型 | 说明 | | --- | --- | --- | | messages | Array | 当前缓存并展示的所有业务消息,最新消息在数组第一项。 | | latestMessage | Object \| null | 最新一条业务消息。 | | message | Object | item 插槽当前渲染的单条消息。 | | index | Number | item 插槽当前消息下标。 | | title | String | 当前通知标题。 | | connected | Boolean | 当前连接状态。 | | close | Function | 关闭当前通知实例。 | | options | Object | 本次 $notify 的默认 options。 |

notificationFormatter 的优先级高于 slot。若同时传入 notificationFormatter 和 slot,会使用 notificationFormatter 返回的通知内容。

导出项

import SystemNotification, {
    install,
    createNotificationSse,
    normalizeNotification,
    parseSseData,
    buildSystemNotifyOptions,
    buildSystemNotificationHtml,
    prependNotification,
    isSystemSignalNotification
} from '@/common/components/SystemNotification/index.js'

| 导出项 | 说明 | | --- | --- | | SystemNotification | 默认导出的 Vue 组件。 | | install | Vue 插件安装函数,内部注册 SystemNotification 组件。 | | createNotificationSse | 创建 SSE 通知连接。 | | normalizeNotification | 将后端通知 envelope 标准化为统一消息对象。 | | parseSseData | 解析 SSE event 的 data 字段,优先按 JSON 解析。 | | buildSystemNotifyOptions | 根据消息列表生成 $notify options。 | | buildSystemNotificationHtml | 根据消息列表生成默认 HTML 内容。 | | prependNotification | 将新通知加入消息列表并按最大数量截断。 | | isSystemSignalNotification | 判断是否为连接或心跳类系统信号。 |

默认消息格式

normalizer 默认会输出以下结构,业务系统可以通过自定义 normalizer 适配自己的后端格式:

{
    id: 'message-id',
    type: 'PRIVATE',
    fromUserId: 'admin',
    toUserId: '10001',
    content: '通知内容',
    sendTime: '2026-07-14 10:00:00',
    code: 'PRIVATE_MESSAGE',
    message: '',
    timestamp: ''
}

CONNECTED 和 HEARTBEAT 这类连接状态消息不会进入通知列表。

注意事项

  • 组件默认依赖当前 Vue 实例存在 $notify。如果接入项目没有 Element UI,请传入 notify。
  • 组件不内置 SSE 地址、token、group、连接 path 或业务事件名。sseUrl 需要传完整连接地址,token 和 eventNames 必须由调用方传入。
  • 默认通知使用 dangerouslyUseHTMLString: true,组件内部会转义消息内容。自定义 notificationFormatter 时需要自行保证内容安全。
  • 默认 duration 为 0,通知不会自动关闭。可以通过 notifyOptions.duration 覆盖。
  • connectionParams 会覆盖同名 query 参数,请谨慎处理 token、userId、group 等字段。