From 4e1193a4ecb6e5be293c48b679df8c57f1966e64 Mon Sep 17 00:00:00 2001 From: tangtaoit Date: Sun, 17 Sep 2023 14:30:05 +0800 Subject: [PATCH] fix: #3 #4 --- .../src/Components/ImageToolbar/index.css | 207 ++++++++++++++++++ .../src/Components/ImageToolbar/index.tsx | 196 +++++++++++++++++ .../src/assets/toolbars/func_image_normal.svg | 20 ++ packages/tsdaodaobase/src/module.tsx | 8 +- 4 files changed, 430 insertions(+), 1 deletion(-) create mode 100644 packages/tsdaodaobase/src/Components/ImageToolbar/index.css create mode 100644 packages/tsdaodaobase/src/Components/ImageToolbar/index.tsx create mode 100644 packages/tsdaodaobase/src/assets/toolbars/func_image_normal.svg diff --git a/packages/tsdaodaobase/src/Components/ImageToolbar/index.css b/packages/tsdaodaobase/src/Components/ImageToolbar/index.css new file mode 100644 index 0000000..c17c3a8 --- /dev/null +++ b/packages/tsdaodaobase/src/Components/ImageToolbar/index.css @@ -0,0 +1,207 @@ + + + +.wk-imagetoolbar { + +} + +.wk-imagetoolbar-content { + padding: 10px; +} + +.wk-imagetoolbar-content-icon img{ + width: 20px; + height: 20px; +} + +.wk-imagetoolbar-content-icon { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; +} + + +.wk-imagedialog { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + align-items: center; + + position: fixed; + z-index: 100; + top: 0; + right: 0; + bottom: 0; + left: 0; + -webkit-app-region: no-drag; + z-index: 100; +} + +.wk-imagedialog-mask { + position: absolute; + background-color: rgba(0,0,0,.3); + left: 0; + right: 0; + top: 0; + bottom: 0; +} + +.wk-imagedialog-content { + width: 420px; + position: relative; + padding: 28px; + background: var(--wk-color-item); + -webkit-box-shadow: 0 2px 8px 0 rgba(0,0,0,.2); + box-shadow: 0 2px 8px 0 rgba(0,0,0,.2); + border-radius: 4px; + overflow: hidden; + -webkit-box-sizing: border-box; + box-sizing: border-box; + max-height: 580px; +} + +.wk-imagedialog-content-close { + position: absolute; + width: 20px; + height: 20px; + top: 28px; + right: 28px; + font-size: 18px; + text-align: center; + border-radius: 50%; + cursor: pointer; + z-index: 1; + line-height: 18px; +} + +.wk-imagedialog-content-close svg { + fill: var(--wk-text-item); +} + +.wk-imagedialog-content-title { + font-size: 16px; + font-weight: 600; +} + +.wk-imagedialog-content-body { + width: 100%; + font-size: 14px; + text-align: center; + margin-top: 28px; +} + +.wk-imagedialog-content-preview { + -webkit-box-sizing: border-box; + box-sizing: border-box; + text-align: left; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + position: relative; + border-radius: 4px 4px 4px 4px; +} + +.wk-imagedialog-content-previewImg { + width: auto; + height: auto; + max-width: 240px; + max-height: 240px; + background-size: auto 100%; + background-position: 50%; + margin: 0 auto 16px; +} + +.wk-imagedialog-content-preview-file { + display: flex; + align-items: center; + width: 100%; + height: 100%; +} + +.wk-imagedialog-content-preview--filecontent { + -webkit-flex: 1 1; + -ms-flex: 1 1; + flex: 1 1; + width: 100%; + height: 78px; + padding: 12px 0px 12px 20px; + overflow: hidden; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: column nowrap; + -ms-flex-flow: column nowrap; + flex-flow: column nowrap; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + background-color: rgb(247, 247, 247); +} + +body[theme-mode=dark] .wk-imagedialog-content-preview--filecontent { + background-color: var(--wk-color-secondary); +} + +.wk-imagedialog-content-preview-file-icon { + height: 78px; + width: 78px; + display: flex; + align-items: center; + border-radius: 0px 0px 0px 10px; +} + +.wk-imagedialog-content-preview-file-thumbnail { + width: 48px; + height: 48px; + margin: auto 16px auto auto; +} + +.wk-imagedialog-content-preview--filecontent-name { + color: var(--wk-text-item); + white-space: nowrap; + overflow-x: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + line-height: normal; +} + +.wk-imagedialog-content-preview--filecontent-size { + font-size: 12px; + color: var(--wk-color-font-tip); +} + +.wk-imagedialog-footer { + margin-top: 28px; + justify-content: flex-end; + display: flex; +} + +.wk-imagedialog-footer button{ + background-color: #fff; + border: 1px solid #dee0e3; + color: #1f2329; + border-radius: 4px; + height: 32px; + min-width: 100px; + padding: 0 16px; + cursor: pointer; +} + +.wk-imagedialog-footer-okbtn { + margin-left: 16px; + color: white !important; + border: none !important; +} + diff --git a/packages/tsdaodaobase/src/Components/ImageToolbar/index.tsx b/packages/tsdaodaobase/src/Components/ImageToolbar/index.tsx new file mode 100644 index 0000000..4adbecc --- /dev/null +++ b/packages/tsdaodaobase/src/Components/ImageToolbar/index.tsx @@ -0,0 +1,196 @@ +import { ConversationContext, FileHelper, ImageContent, WKApp } from "@tsdaodao/base"; +import React from "react"; +import { Component, ReactNode } from "react"; + +import "./index.css" + + +interface ImageToolbarProps { + conversationContext: ConversationContext + icon: string +} + +interface ImageToolbarState { + showDialog: boolean + file?: any + fileType?: string + previewUrl?: any, + fileIconInfo?: any, + canSend?: boolean + width?: number + height?: number +} + +export default class ImageToolbar extends Component{ + pasteListen!:(event:any)=>void + constructor(props:any) { + super(props) + this.state = { + showDialog: false, + } + } + + componentDidMount() { + let self = this; + + const { conversationContext } = this.props + + this.pasteListen = function (event:any) { // 监听粘贴里的文件 + let files = event.clipboardData.files; + if (files.length > 0) { + self.showFile(files[0]); + } + } + document.addEventListener('paste',this.pasteListen ) + + conversationContext.setDragFileCallback((file)=>{ + self.showFile(file); + }) + } + + componentWillUnmount() { + document.removeEventListener("paste",this.pasteListen) + } + + $fileInput: any + onFileClick = (event: any) => { + event.target.value = '' // 防止选中一个文件取消后不能再选中同一个文件 + } + onFileChange() { + let file = this.$fileInput.files[0]; + this.showFile(file); + } + chooseFile = () => { + this.$fileInput.click(); + } + showFile(file: any) { + const self = this + if (file.type && file.type.startsWith('image/')) { + var reader = new FileReader(); + reader.readAsDataURL(file); + reader.onloadend = function (e: any) { + self.setState({ + file: file, + fileType: "image", + previewUrl: reader.result, + showDialog: true, + }); + }; + } + + } + + onSend() { + const {conversationContext} = this.props + const { file, previewUrl,width,height,fileType } = this.state + if(fileType === "image") { + conversationContext.sendMessage(new ImageContent(file,previewUrl,width,height)) + } + + this.setState({ + showDialog: false, + }); + } + onPreviewLoad(e: any) { + let img = e.target; + let width = img.naturalWidth || img.width; + let height = img.naturalHeight || img.height; + this.setState({ + width: width, + height: height, + canSend: true, + }); + } + render(): ReactNode { + const { icon } = this.props + const { showDialog, canSend, fileIconInfo, file, fileType, previewUrl } = this.state + return
+
{ + this.chooseFile() + }}> +
+ + { this.$fileInput = ref }} type="file" multiple={false} accept="image/*" style={{ display: 'none' }} /> +
+
+ { + showDialog ? ( + { + this.setState({ + showDialog: !showDialog + }) + }} /> + ) : null + } +
+ } +} + + +interface ImageDialogProps { + onClose: () => void + onSend?: () => void + fileType?: string // image, file + previewUrl?: string + file?: any + fileIconInfo?: any, + canSend?: boolean + onLoad: (e: any) => void +} + +class ImageDialog extends Component { + + + // 格式化文件大小 + getFileSizeFormat(size: number) { + if (size < 1024) { + return `${size} B` + } + if (size > 1024 && size < 1024 * 1024) { + return `${(size / 1024).toFixed(2)} KB` + } + if (size > 1024 * 1024 && size < 1024 * 1024 * 1024) { + return `${(size / 1024 / 1024).toFixed(2)} M` + } + return `${(size / (1024 * 1024 * 1024)).toFixed(2)}G` + } + + render() { + const { onClose, onSend, fileType, previewUrl, file, canSend, fileIconInfo, onLoad } = this.props + return
+
+
+
+ +
+
发送{fileType === 'image' ? '图片' : '文件'}
+
+ { + fileType === 'image' ? ( +
+ +
+ ) : ( +
+
+
+ +
+
+
{file?.name}
+
{this.getFileSizeFormat(file?.size)}
+
+
+
+ ) + } +
+ + +
+
+ +
+
+ } +} \ No newline at end of file diff --git a/packages/tsdaodaobase/src/assets/toolbars/func_image_normal.svg b/packages/tsdaodaobase/src/assets/toolbars/func_image_normal.svg new file mode 100644 index 0000000..8335865 --- /dev/null +++ b/packages/tsdaodaobase/src/assets/toolbars/func_image_normal.svg @@ -0,0 +1,20 @@ + + + pc_picture + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/tsdaodaobase/src/module.tsx b/packages/tsdaodaobase/src/module.tsx index c1cc0f0..c6bece3 100644 --- a/packages/tsdaodaobase/src/module.tsx +++ b/packages/tsdaodaobase/src/module.tsx @@ -42,6 +42,7 @@ import { TypingManager } from "./Service/TypingManager"; import APIClient from "./Service/APIClient"; import { ChannelAvatar } from "./Components/ChannelAvatar"; import { ScreenshotCell, ScreenshotContent } from "./Messages/Screenshot"; +import ImageToolbar from "./Components/ImageToolbar"; export default class BaseModule implements IModule { id(): string { @@ -337,10 +338,15 @@ export default class BaseModule implements IModule { WKApp.endpoints.registerChatToolbar("chattoolbar.screenshot", (ctx) => { return { - window.open("https://jietu.qq.com") + window.open("https://www.snipaste.com") }}> }) + WKApp.endpoints.registerChatToolbar("chattoolbar.image", (ctx) => { + + return + }) + }