mirror of
https://github.com/TangSengDaoDao/TangSengDaoDaoWeb
synced 2025-06-04 08:08:44 +00:00
parent
9c0c9f90f0
commit
4e1193a4ec
207
packages/tsdaodaobase/src/Components/ImageToolbar/index.css
Normal file
207
packages/tsdaodaobase/src/Components/ImageToolbar/index.css
Normal file
@ -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;
|
||||
}
|
||||
|
196
packages/tsdaodaobase/src/Components/ImageToolbar/index.tsx
Normal file
196
packages/tsdaodaobase/src/Components/ImageToolbar/index.tsx
Normal file
@ -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<ImageToolbarProps, ImageToolbarState>{
|
||||
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 <div className="wk-imagetoolbar" >
|
||||
<div className="wk-imagetoolbar-content" onClick={() => {
|
||||
this.chooseFile()
|
||||
}}>
|
||||
<div className="wk-imagetoolbar-content-icon">
|
||||
<img src={icon}></img>
|
||||
<input onClick={this.onFileClick} onChange={this.onFileChange.bind(this)} ref={(ref) => { this.$fileInput = ref }} type="file" multiple={false} accept="image/*" style={{ display: 'none' }} />
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
showDialog ? (
|
||||
<ImageDialog onSend={this.onSend.bind(this)} onLoad={this.onPreviewLoad.bind(this)} canSend={canSend} fileIconInfo={fileIconInfo} file={file} fileType={fileType} previewUrl={previewUrl} onClose={() => {
|
||||
this.setState({
|
||||
showDialog: !showDialog
|
||||
})
|
||||
}} />
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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<ImageDialogProps> {
|
||||
|
||||
|
||||
// 格式化文件大小
|
||||
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 <div className="wk-imagedialog">
|
||||
<div className="wk-imagedialog-mask" onClick={onClose}></div>
|
||||
<div className="wk-imagedialog-content">
|
||||
<div className="wk-imagedialog-content-close" onClick={onClose}>
|
||||
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2683" ><path d="M568.92178541 508.23169412l299.36805789-299.42461715a39.13899415 39.13899415 0 0 0 0-55.1452591L866.64962537 152.02159989a39.13899415 39.13899415 0 0 0-55.08869988 0L512.19286756 451.84213173 212.76825042 151.90848141a39.13899415 39.13899415 0 0 0-55.0886999 0L155.98277331 153.54869938a38.46028327 38.46028327 0 0 0 0 55.08869987L455.46394971 508.23169412 156.03933259 807.71287052a39.13899415 39.13899415 0 0 0 0 55.08869986l1.64021795 1.6967772a39.13899415 39.13899415 0 0 0 55.08869988 0l299.42461714-299.48117638 299.36805793 299.42461714a39.13899415 39.13899415 0 0 0 55.08869984 0l1.6967772-1.64021796a39.13899415 39.13899415 0 0 0 0-55.08869987L568.86522614 508.17513487z" p-id="2684"></path></svg>
|
||||
</div>
|
||||
<div className="wk-imagedialog-content-title">发送{fileType === 'image' ? '图片' : '文件'}</div>
|
||||
<div className="wk-imagedialog-content-body">
|
||||
{
|
||||
fileType === 'image' ? (
|
||||
<div className="wk-imagedialog-content-preview">
|
||||
<img alt="" className="wk-imagedialog-content-previewImg" src={previewUrl} onLoad={onLoad} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="wk-imagedialog-content-preview">
|
||||
<div className="wk-imagedialog-content-preview-file">
|
||||
<div className="wk-imagedialog-content-preview-file-icon" style={{ backgroundColor: fileIconInfo?.color }}>
|
||||
<img alt="" className="wk-imagedialog-content-preview-file-thumbnail" src={fileIconInfo?.icon} />
|
||||
</div>
|
||||
<div className="wk-imagedialog-content-preview--filecontent">
|
||||
<div className="wk-imagedialog-content-preview--filecontent-name">{file?.name}</div>
|
||||
<div className="wk-imagedialog-content-preview--filecontent-size">{this.getFileSizeFormat(file?.size)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className="wk-imagedialog-footer" >
|
||||
<button onClick={onClose}>取消</button>
|
||||
<button onClick={onSend} className="wk-imagedialog-footer-okbtn" disabled={!canSend} style={{ backgroundColor: canSend ? WKApp.config.themeColor : 'gray' }}>发送</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>pc_picture</title>
|
||||
<defs>
|
||||
<linearGradient x1="100%" y1="86.521053%" x2="0%" y2="13.478947%" id="linearGradient-1">
|
||||
<stop stop-color="#F6603E" offset="0%"></stop>
|
||||
<stop stop-color="#F17C55" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="PC切图" transform="translate(-93.000000, -37.000000)">
|
||||
<g id="pc_picture" transform="translate(93.000000, 37.000000)">
|
||||
<rect id="矩形备份-2" fill="#FFFFFF" opacity="0.01" x="0" y="0" width="20" height="20"></rect>
|
||||
<g id="tupian-2" transform="translate(0.000000, 1.000000)" fill="url(#linearGradient-1)">
|
||||
<path d="M18.2324639,0 C19.2073943,0 20,0.792605723 20,1.76753609 L20,1.76753609 L20,15.3253988 C20,16.3003292 19.2073943,17.0929349 18.2324639,17.0929349 L18.2324639,17.0929349 L1.76753609,17.0929349 C1.26614333,17.0929349 0.815396303,16.9232717 0.496328184,16.6143327 C0.177260066,16.3028615 0,15.8622436 0,15.3760446 L0,15.3760446 L0,1.81818182 C0,1.32945049 0.182324639,0.871106609 0.511521904,0.531780198 C0.845783743,0.189921499 1.29146619,0 1.76753609,0 L1.76753609,0 Z M18.2299316,1.22815903 L1.7650038,1.22815903 C1.46366169,1.22815903 1.22562674,1.48645227 1.22562674,1.81818182 L1.22562674,1.81818182 L1.22815903,1.81818182 L1.22815903,15.3760446 C1.22815903,15.7786781 1.52190428,15.8647759 1.76753609,15.8647759 L1.76753609,15.8647759 L18.2299316,15.8647759 C18.5262092,15.8647759 18.7693087,15.6242087 18.7693087,15.3253988 L18.7693087,15.3253988 L18.7693087,1.76753609 C18.7693087,1.47125855 18.5287415,1.22815903 18.2299316,1.22815903 L18.2299316,1.22815903 Z M5.96859965,5.60901494 C6.15598886,5.58622436 6.34084578,5.64953153 6.47758926,5.78121043 L6.47758926,5.78121043 L6.48771841,5.79133958 L11.3876931,10.9141555 L13.8389466,8.48569258 C14.0541909,8.27551279 14.3808559,8.24259306 14.6315523,8.40465941 L14.6315523,8.40465941 L14.6594074,8.42238541 L14.6746012,8.43757913 L17.5993923,11.2560142 C17.8703469,11.4560648 17.9412509,11.8308433 17.7589263,12.1195239 C17.6652317,12.261332 17.5234237,12.3600912 17.3562927,12.3955432 C17.1916941,12.4309952 17.0194986,12.4006077 16.8776906,12.3069131 L16.8776906,12.3069131 L16.8473031,12.2866548 L16.8219802,12.261332 L14.3251456,9.79994935 L11.8308433,12.2689288 C11.7118258,12.385414 11.5522917,12.4512535 11.3851608,12.4512535 C11.2205622,12.4512535 11.0635604,12.3879463 10.9445429,12.2739934 L10.9445429,12.2739934 L10.9344138,12.2638643 L6.13573056,7.24487212 L3.01848569,11.970119 C2.82349962,12.2638643 2.42593062,12.3423652 2.13471765,12.1473791 C1.9929096,12.0536845 1.8966827,11.9093441 1.86376298,11.7422132 C1.83084325,11.5750823 1.86629526,11.4054191 1.95998987,11.263611 L1.95998987,11.263611 L5.50772347,5.89009876 C5.61154723,5.7305647 5.78121043,5.62927323 5.96859965,5.60901494 Z M12.1625728,3.99088377 C12.5018992,3.99088377 12.818435,4.12762725 13.0539377,4.37072677 C13.2894404,4.61635857 13.4109901,4.93795898 13.3983287,5.27728539 C13.3730058,5.91795391 12.8513548,6.43960496 12.2106862,6.46239554 L12.2106862,6.46239554 L12.1625728,6.46239554 C11.8409724,6.46239554 11.537098,6.34084578 11.3041276,6.11547227 C11.0610281,5.8825019 10.9268169,5.56596607 10.9268169,5.22663966 C10.9268169,4.54545455 11.4813877,3.99088377 12.1625728,3.99088377 Z" id="形状"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
@ -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 <IconClick icon={require("./assets/toolbars/func_screenshot.svg").default} onClick={() => {
|
||||
window.open("https://jietu.qq.com")
|
||||
window.open("https://www.snipaste.com")
|
||||
}}></IconClick>
|
||||
})
|
||||
|
||||
WKApp.endpoints.registerChatToolbar("chattoolbar.image", (ctx) => {
|
||||
|
||||
return <ImageToolbar icon={require("./assets/toolbars/func_image_normal.svg").default} conversationContext={ctx}></ImageToolbar>
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user