add notify editor/view

This commit is contained in:
Timi
2026-08-24 16:50:40 +08:00
parent ccbb924c9a
commit 0c5a4d4fd9
8 changed files with 155 additions and 3 deletions
+1
View File
@@ -78,6 +78,7 @@ const formattedValue = computed(() => formatNumber(String(props.value ?? "").tri
<style lang="less" scoped> <style lang="less" scoped>
.tui-contact-number { .tui-contact-number {
width: fit-content;
.icon { .icon {
display: inline-flex; display: inline-flex;
+12 -2
View File
@@ -16,6 +16,8 @@ import TiptapEditor from "./tiptap-editor";
import TiptapView from "./tiptap-view"; import TiptapView from "./tiptap-view";
import ArticleEditor from "./article-editor"; import ArticleEditor from "./article-editor";
import ArticleView from "./article-view"; import ArticleView from "./article-view";
import NotifyEditor from "./notify-editor";
import NotifyView from "./notify-view";
export default [ export default [
Icon, Icon,
@@ -34,7 +36,9 @@ export default [
TiptapEditor, TiptapEditor,
TiptapView, TiptapView,
ArticleEditor, ArticleEditor,
ArticleView ArticleView,
NotifyEditor,
NotifyView
]; ];
export { export {
@@ -54,7 +58,9 @@ export {
TiptapEditor, TiptapEditor,
TiptapView, TiptapView,
ArticleEditor, ArticleEditor,
ArticleView ArticleView,
NotifyEditor,
NotifyView
}; };
export type { export type {
@@ -71,3 +77,7 @@ export type {
export type { export type {
ArticleEditorExpose ArticleEditorExpose
} from "./article-editor"; } from "./article-editor";
export type {
NotifyEditorExpose
} from "./notify-editor";
+11
View File
@@ -0,0 +1,11 @@
import view from "./index.vue";
import Toolkit from "../../utils/Toolkit";
import type { NotifyDetail } from "../../types";
export type NotifyEditorExpose = {
submit: () => Promise<NotifyDetail>;
getPendingMediaCount: () => number;
};
export const NotifyEditor = Toolkit.withInstall(view);
export default NotifyEditor;
+77
View File
@@ -0,0 +1,77 @@
<template>
<article-editor
ref="articleEditorRef"
:model-value="articleValue"
:placeholder="placeholder"
:upload-image="uploadImage"
:upload-video="uploadVideo"
:resolve-attachment-url="resolveAttachmentUrl"
:resolve-temp-url="resolveTempUrl"
@change="onArticleChange"
@update:model-value="onArticleCommit"
/>
</template>
<script lang="ts" setup>
import type { Article, ArticleContentType, Attachment, NotifyDetail } from "../../types";
import type { ArticleEditorExpose } from "../article-editor";
import { ArticleEditor } from "../article-editor";
import type { UploadResult } from "../tiptap-editor";
import { normalizeNotifyContentType } from "../notify/shared";
defineOptions({
name: "NotifyEditor"
});
const props = withDefaults(defineProps<{
modelValue?: NotifyDetail;
placeholder?: string;
uploadImage?: (file: File) => Promise<UploadResult>;
uploadVideo?: (file: File) => Promise<UploadResult>;
resolveAttachmentUrl?: (attachmentId: string, attachment?: Attachment) => string | Promise<string>;
resolveTempUrl?: (tempFileId: string) => string | Promise<string>;
}>(), {
modelValue: () => ({}),
placeholder: "请输入通知内容"
});
const emit = defineEmits<{
change: [value: NotifyDetail];
"update:modelValue": [value: NotifyDetail];
}>();
const articleEditorRef = ref<ArticleEditorExpose>();
const articleValue = computed<Article>(() => ({
contentType: normalizeNotifyContentType(props.modelValue?.contentType) as ArticleContentType,
content: props.modelValue?.content || "",
attachmentList: props.modelValue?.attachmentList
}));
function buildNotifyValue(value: Article): NotifyDetail {
return {
...(props.modelValue || {}),
contentType: normalizeNotifyContentType(value.contentType),
content: value.content || "",
attachmentList: value.attachmentList || []
};
}
function onArticleChange(value: Article) {
emit("change", buildNotifyValue(value));
}
function onArticleCommit(value: Article) {
emit("update:modelValue", buildNotifyValue(value));
}
async function submit() {
const value = await articleEditorRef.value?.submit();
return buildNotifyValue(value || articleValue.value);
}
defineExpose({
submit,
getPendingMediaCount: () => articleEditorRef.value?.getPendingMediaCount() || 0
});
</script>
+5
View File
@@ -0,0 +1,5 @@
import view from "./index.vue";
import Toolkit from "../../utils/Toolkit";
export const NotifyView = Toolkit.withInstall(view);
export default NotifyView;
+32
View File
@@ -0,0 +1,32 @@
<template>
<article-view
:article="articleValue"
:empty-text="emptyText"
:resolve-attachment-url="resolveAttachmentUrl"
/>
</template>
<script lang="ts" setup>
import type { Article, ArticleContentType, Attachment, NotifyDetail } from "../../types";
import { ArticleView } from "../article-view";
import { normalizeNotifyContentType } from "../notify/shared";
defineOptions({
name: "NotifyView"
});
const props = withDefaults(defineProps<{
detail?: NotifyDetail;
emptyText?: string;
resolveAttachmentUrl?: (attachmentId: string, attachment?: Attachment) => string | Promise<string>;
}>(), {
detail: () => ({}),
emptyText: "暂无通知内容"
});
const articleValue = computed<Article>(() => ({
contentType: normalizeNotifyContentType(props.detail?.contentType) as ArticleContentType,
content: props.detail?.content || "",
attachmentList: props.detail?.attachmentList
}));
</script>
+8
View File
@@ -0,0 +1,8 @@
import type { NotifyContentType } from "../../types";
export const DEFAULT_NOTIFY_CONTENT_TYPE: NotifyContentType = "TIP_TAP";
/** 规范化通知内容类型,兼容旧通知未返回内容类型的情况 */
export function normalizeNotifyContentType(contentType?: string): NotifyContentType {
return contentType === "TEXT" ? "TEXT" : DEFAULT_NOTIFY_CONTENT_TYPE;
}
+9 -1
View File
@@ -1,4 +1,5 @@
import type { Model } from "./Model"; import type { Model } from "./Model";
import type { Attachment } from "./Attachment";
import type { User } from "./User"; import type { User } from "./User";
/** 通知消息类型 */ /** 通知消息类型 */
@@ -7,6 +8,9 @@ export type NotifyMsgType = "INTERNAL" | "SMS" | "MAIL" | "HTTP" | "WECHAT";
/** 通知投递状态 */ /** 通知投递状态 */
export type NotifyStatus = "WAITING" | "REMINDED" | "TIMEOUT" | "CANCEL" | "FAIL"; export type NotifyStatus = "WAITING" | "REMINDED" | "TIMEOUT" | "CANCEL" | "FAIL";
/** 通知内容类型 */
export type NotifyContentType = "TEXT" | "TIP_TAP";
/** 通知 */ /** 通知 */
export type Notify = { export type Notify = {
/** 业务类型 */ /** 业务类型 */
@@ -34,7 +38,11 @@ export type NotifyDetail = {
/** 标题 */ /** 标题 */
subject?: string; subject?: string;
/** 内容 */ /** 内容 */
data?: string; content?: string;
/** 内容类型 */
contentType?: NotifyContentType;
/** 媒体附件列表 */
attachmentList?: Attachment[];
/** 参数 */ /** 参数 */
args?: string; args?: string;
/** 发送来源 */ /** 发送来源 */