master
Qihua Pan 2 years ago
parent cb1da01788
commit 855fbf1b96
  1. 5
      .gitignore
  2. 6
      README.md
  3. 11735
      package-lock.json
  4. 5
      package.json
  5. 198
      src/app.tsx
  6. 12
      src/baidu.ts
  7. 20
      src/components/Translate.tsx
  8. 30
      src/components/dog.js
  9. 30
      src/components/stepone.tsx
  10. 53
      src/components/steptwo.tsx
  11. 2
      src/index.tsx
  12. 40
      src/tencent.ts
  13. 7
      webpack.config.js

5
.gitignore vendored

@ -1,3 +1,6 @@
node_modules
dist/
*.log
*.log
.env
deploy
.idea

@ -1,8 +1,4 @@
# react-nodegui-starter
**Clone and run for a quick way to see React NodeGui in action.**
<img alt="logo" src="https://github.com/nodegui/react-nodegui-starter/raw/master/assets/demo.png" height="500" />
# 简陋翻译工具
## To Use

11735
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -13,8 +13,10 @@
},
"dependencies": {
"@nodegui/react-nodegui": "^0.10.2",
"node-fetch": "^2.6.7",
"open": "^7.2.1",
"react": "^16.13.1"
"react": "^16.13.1",
"tencentcloud-sdk-nodejs": "^4.0.331"
},
"devDependencies": {
"@babel/core": "^7.11.6",
@ -27,6 +29,7 @@
"@types/webpack-env": "^1.15.3",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"dotenv-webpack": "^7.1.0",
"file-loader": "^6.1.0",
"fork-ts-checker-webpack-plugin": "^5.2.0",
"native-addon-loader": "^2.0.1",

@ -1,50 +1,164 @@
import { Text, Window, hot, View } from "@nodegui/react-nodegui";
import {Button, ComboBox, hot, PlainTextEdit, Text, View, Window} from "@nodegui/react-nodegui";
import React from "react";
import { QIcon } from "@nodegui/nodegui";
import { StepOne } from "./components/stepone";
import { StepTwo } from "./components/steptwo";
import {AlignmentFlag, QIcon} from "@nodegui/nodegui";
import nodeguiIcon from "../assets/nodegui.jpg";
import {RNComboBox} from "@nodegui/react-nodegui/dist/components/ComboBox/RNComboBox";
import {RNText} from "@nodegui/react-nodegui/dist/components/Text/RNText";
import tencentTmt from "./tencent";
import {RNPlainTextEdit} from "@nodegui/react-nodegui/dist/components/PlainTextEdit/RNPlainTextEdit";
import baiduTranslate from "./baidu"
import Translate from "./components/Translate";
const minSize = { width: 500, height: 520 };
const winIcon = new QIcon(nodeguiIcon);
class App extends React.Component {
render() {
return (
<Window
windowIcon={winIcon}
windowTitle="Hello 👋🏽"
minSize={minSize}
styleSheet={styleSheet}
>
<View style={containerStyle}>
<Text id="welcome-text">Welcome to NodeGui 🐕</Text>
<Text id="step-1">1. Play around</Text>
<StepOne />
<Text id="step-2">2. Debug</Text>
<StepTwo />
</View>
</Window>
);
}
}
class App extends React.Component<any, any> {
private getComboBoxHandler: (key: ("sourceIndex" | "targetIndex")) => (index: number) => void;
private sourceLang: Array<{text:string,lang:string}>;
private lang: { [key: string]: string };
private sourceRef: React.RefObject<RNComboBox>;
private targetRef:React.RefObject<RNComboBox>;
private labelRef: React.RefObject<RNText>;
private sourceTextRef: React.RefObject<RNPlainTextEdit>;
private tencentTextRef: React.RefObject<RNPlainTextEdit>;
private baiduTextRef: React.RefObject<RNPlainTextEdit>;
constructor(props: any) {
super(props);
this.state = {
additionalButtons: [],
size: {height: 300, width: 900},
allow:false,
}
this.sourceRef=React.createRef<RNComboBox>()
this.targetRef=React.createRef<RNComboBox>()
this.labelRef=React.createRef<RNText>()
this.sourceTextRef=React.createRef<RNPlainTextEdit>()
this.tencentTextRef=React.createRef<RNPlainTextEdit>()
this.baiduTextRef=React.createRef<RNPlainTextEdit>()
this.getComboBoxHandler = (key: 'sourceIndex' | 'targetIndex') => {
return (index: number) => {
let ref
if(key==='sourceIndex'){
ref=this.targetRef
}else{
ref=this.sourceRef
}
if (ref.current?.currentIndex() === index) {
for(let langIndex in this.sourceLang){
if(+langIndex!==index){
ref.current?.setCurrentIndex(+langIndex)
break
}
}
}
// console.debug(`${key}选中${this.sourceLang[index].text}`)
}
}
this.lang = {
'en': '英语',
'zh': '中文'
}
this.sourceLang = []
for (let key in this.lang) {
this.sourceLang.push({text: this.lang[key],lang:key})
}
}
componentDidMount() {
this.labelRef.current?.setAlignment(AlignmentFlag.AlignCenter)
}
render() {
const winIcon = new QIcon(nodeguiIcon);
const comboHandler = {
currentIndexChanged: this.getComboBoxHandler("sourceIndex"),
}
const targetComboHandler = {
currentIndexChanged: this.getComboBoxHandler("targetIndex")
}
const sourceHandler={
textChanged:()=>{
let flag=(this.sourceTextRef.current?.toPlainText().length||0)>0
this.setState({allow:flag})
}
}
const translateHandler={
clicked:()=>{
let source=this.sourceTextRef.current?.toPlainText()||''
let target=this.sourceLang[this.targetRef.current?.currentIndex()||0].lang
tencentTmt(source,target)
.then(res=>{
console.info(res);
this.tencentTextRef.current?.setPlainText(res.TargetText)
}).catch(err=>console.error("error", err))
const containerStyle = `
flex: 1;
`;
baiduTranslate(source,target).then((res: { text: () => any; })=>res.text()).then((res: string)=>{
console.info(res)
let r=JSON.parse(res)
this.baiduTextRef.current?.setPlainText(r["trans_result"][0]["dst"])
}).catch((err: any)=>console.error("error", err))
}
}
return (
<Window styleSheet={styleSheet}
windowIcon={winIcon} size={this.state.size}>
<View id="rootView">
<View style={`flex-direction:row`}>
<ComboBox style={`flex:1;`} items={this.sourceLang.concat({lang:'auto',text:'自动'})} currentIndex={0}
on={comboHandler} ref={this.sourceRef}></ComboBox>
<ComboBox style={`flex:1;`} items={this.sourceLang} currentIndex={1}
on={targetComboHandler} ref={this.targetRef}></ComboBox>
</View>
<View style={`flex-direction:row;flex:1;`}>
<View style={`flex-direction:column;flex:1;`}>
<PlainTextEdit placeholderText='请输入原文'
ref={this.sourceTextRef}
on={sourceHandler}
style={`flex:9`}
></PlainTextEdit>
<Text id="tencent" style={`flex:1`} ref={this.labelRef}></Text>
</View>
<Translate targetTextRef={this.tencentTextRef} name={"腾讯翻译"} href={"https://cloud.tencent.com/product/tmt"}/>
<Translate targetTextRef={this.baiduTextRef} name={"百度翻译"} href={"https://api.fanyi.baidu.com/"}/>
</View>
<View style={`align-items:'center';padding:5px`}>
<Button enabled={this.state.allow} text="翻译" on={translateHandler}></Button>
</View>
</View>
</Window>
);
}
}
const styleSheet = `
#welcome-text {
font-size: 24px;
padding-top: 20px;
qproperty-alignment: 'AlignHCenter';
font-family: 'sans-serif';
}
#step-1, #step-2 {
font-size: 18px;
padding-top: 10px;
padding-horizontal: 20px;
}
`;
#rootView{
height: '100%';
flex-direction:'column';
}
QPushButton{
width:300;
color:red;
font-size:20px;
background-color:gray;
border-radius:10px;
}
QPushButton:pressed{
background-color:gray;
}
QPushButton:enabled{
background-color: white;
}
`
export default hot(App);

@ -0,0 +1,12 @@
const fetch = require("node-fetch");
const MD5 = require('md5.js');
function md5(message:string){
return new MD5().update(message).digest('hex')
}
export default async function baiduTranslate(q:string,to:string){
let salt=Math.round(Math.random()*new Date().getTime())
let sign=md5(process.env.REACT_APP_BAIDU_APP_ID+q+salt+process.env.REACT_APP_BAIDU_SECRET_KEY)
return fetch.default(`http://api.fanyi.baidu.com/api/trans/vip/translate?q=${q}&from=auto&to=${to}&appid=${process.env.REACT_APP_BAIDU_APP_ID}&salt=${salt}&sign=${sign}`)
}

@ -0,0 +1,20 @@
import {PlainTextEdit, Text, View} from "@nodegui/react-nodegui";
import React, {useEffect, useRef} from "react";
import {RNPlainTextEdit} from "@nodegui/react-nodegui/dist/components/PlainTextEdit/RNPlainTextEdit";
import {RNText} from "@nodegui/react-nodegui/dist/components/Text/RNText";
import {AlignmentFlag} from "@nodegui/nodegui";
export default function Translate(props: {targetTextRef:React.RefObject<RNPlainTextEdit>,name:string,href:string}){
const labelRef=useRef<RNText>()
useEffect(() => {
labelRef.current?.setAlignment(AlignmentFlag.AlignCenter)
});
return <View style={`flex-direction:column;flex:1;`}>
<PlainTextEdit style={`flex:9`} readOnly={true} ref={props.targetTextRef}></PlainTextEdit>
<Text openExternalLinks={true} ref={labelRef} style={`flex:1;text-align:center;`} on={{
}}>{"<a href=\""+props.href+"\">"+props.name}</Text>
</View>
}

@ -1,30 +0,0 @@
module.exports = `data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAADIAAAAzCAMAAADivasmAAACglBMVEUAAABPPz9RQj9QQUBPPj5PPz5TRENQQEBTQUFRQUFRQUFTQ0J
RQUFQQEBkWFdXSkpVSUhTQEFRQUFUQUFQQUBQQEBOPj1QQD9WQkVdUE5RRERZTEtWRkdRQUFQQEBQQEBSQUJRQUFRQEBURUVQQEBPPT
1aRUVOPT1VRkZPQUBRQEBRQUBSQUFPQEBTQUFSQ0NRQEFQQkFUPkFUQkNOPz5RPj9SP0BTRERVRUVUQEFWRUX///+jnpz+//6lnpykn
pz9/f2inpyinZv9/v2kn538+/v///6inpv7+vmknZv+/v/+/f22s7KnoJ6Xj4+ln51SQkJVP0ClnZtTQEK0sa6koJ38/Pr19fTT0c+m
oJ6Gfn3w7u6wrKuhm5lOQkBSPj/l5OLBv72sp6Wmo6CdlpSblJL6f4f3gIRRQD9NQD78/fv5+fbz8/Lr6enf3dzFw8G/u7qkm5qgmJa
MhoV6cnF/T1D8/f3y8vLt7Ovh4N/a2NjLx8bIxsWuqaeknp+nn52akZCTi4mRiIePhoV/eXjKcnh5bWxrYmFpYF9gVVVdUVJYSklMPT
v49/jl5OTd29nOy8rMycjHxsS8uLaopKShmZf2goiLg4KIfn2DeXh5cG9yaGdvZmVoXV1cTU9VR0dLPzry8O/p6Ofm5ubZ1tbW1NLS0
tHRzszDwb+6trS1srD4gomEfXt1a2q0ZWpsZWT29/Xo5ePV0tO7ubicl5iYkpKVjY3ofofde4KMgYHNc3qtZmuRWFhZTUxPQ0RbQEG1
sLGyrq6qqqr4gYbzhYXpfYXxhISJfHx+dHPDbnO/bnOAcnK+bHG5a3B0bGy2ZWqgZGicWVxlXFqWXFlyUE9KQ0GhVcLgAAAAO3RSTlM
AB9CjBc/izbaqZzInDf759e7DbmtGGxYR+fj29cm5rpaNXT07A/3669mdkoV7elJPLSHz6d3UfV9UML/w/okAAAWkSURBVEjHhZZlVx
tBFIaXUqAtdXd3d8vszmp2kwBpQkJKDCkUd0pb3IpTijvU3d3dXf9PZzbaNpy+n/acO8+ZK+/chHBq8taoLZOWEENp/YiGCfOnE96a1
1cK4y9NnOYbWLJgbFlWdvcE7/D6aiNDUdqLgaN9EWNG9tmgFlJvVhIejSgHkRl7AF8euOhfYurEXIZlY4p4/bLhhFsBYfA8d6AIsKWz
tv9NLArsAiLVRO8Gobu8kGFhqhAFmWoQqezabcP/IKaMKwOqPdEaNUb8/kTUCkVKDOCz+uYFL1w9NyggIGji/FHBawbieWC1cErSJ0J
y0hGWonIH+7orwuLjS8vNVY/62wE4Z9JofCIKJGVEPqVS6bUqIIsvofR6NkNCkSERWkqPpbCgg4EQaItPN9LkkAh9IITVeiMMw7AQxh
6klT4RkpRORQJfgmzGVR8IG0JLLSLwKR6KBhN3mvkHkWJYMJQgtJr+RuJhSAEKoSJUFAUckmtyVAUBb2iGoYNjvAwTB5HDcJCNDImJs
TIOiI3NjGltc14eCUMHFruJ4cvaoXzKGJt/lVMqNZYCxIut1yRSyUnR50Un1O9B/HaFyveLISaOIxUKNak53sacitCQaCAcLRU623Bp
oRtZ3M8DrKJUdMYh8ng+7frWSJlGXBBzcZUbmZQLZEXTCrdIr0/StEdGbs91IyNzRJzWuQhSofYc9bBk8hmMAP3MMa5SJthkd6RzKCp
o8jgPRguCgJlUESFU6JV1rldUIxtlTwqnIKXMysp8wUVwaW1XbqZqUGqZGKE6/d15AawMDacQCjMK7HXHSLUDicg5lpPIRqCvJhnJmu
1YTBvHsRRuYboSpdU2UBueeNJ1i+lRTZKuBzdlPyuPoWeNjCzo5jHCpqKc8zISdbp6iwsRzFE63U2McFaMiPEr8GYYPd4GMGJNxu0Ur
N97rgvu6k/k1N2yYCSvQEb4S6PwDuviIUYKOXxILUg0zSmUSGq1mkSjp+WRcgdle1Jxy8cQC/uLKWxeMZ1WDC3cZoeju/2JQHOkFiPs
ftITpzlSo9HQ3mbgrPLsjKV2InhOrh6iPA0aT1x54JzBYHgacpL0Qo7gJ1TSuXwKmn1ANQ+Y4utqD6Ix66KiUK/CD3vdk8aiVO5tmib
vcDMDQPF+hUf0xXAkXXiSl09JwYBqiZuNXRY8I4sBtsI8TxJqrqkWM3aD4IXQuGclH9aiFxlUoaLgs0YXoUy2SHmk5UZvfVFThJo7Jr
mpiLNQD22zRhOTe1V6isqU3EiavVBADr5Vm5JMk8cftwhuZDdaHvy9EcSksXF4k2SaXMyBpGKENH7edQ25tDmx2YmQEmoZ1LJVIwli1
LhyLaofbVGalMs0DzZLJmuiPceSnF5dt9/Z47QQBhHxY1f5YZMFvAUQjf9Gmpy2cLhmV/3jcN0T+6+BpMEznEyYWkQegpKKpTucO8m/
JptisAEcDUs/+vPh+wf77n/bl9jq2B/CcxSm9D1zNhJO4eSMenBK40ji8NFDLxK+7HvZcUcXo3Q6OhKC0rELvH8T59exVKRrcCca7if
svbyv48Ir3Vm1YyaWIhHkziG8FVSm5QF/JCUZp45uwciFvXdkhKalfLSU+PZZU72RwM4SlK0q9mQjbrMLuRtVgBITDhoYgJCw+g3eyN
QVlUZeXjMt0SkpV3683nvoQcfeT1HREWlnYkXAMCqqc8IU4g/5jazOptALQn1Rnb8d9bEjISHh7tKqViMAPH6N2b0TdxJ/a/J4M4WjE
DJ8r/3hu0Nfn9S2Q4AlZnWNn+zrb9QG/xlmG3p3AIr6rrqkpPrcLIik0trMM/ynEb4V7D++qow1AkYE2uw4VssyjJEtq5q9cjQxtKaP
mjuzurIiLM5GUWxcWEVlzcx5k6YS/5HforX+iUkNDQ32zUH+q9dN/+fAbySYWHnCNGc5AAAAAElFTkSuQmCC`;

@ -1,30 +0,0 @@
import { Text, View } from "@nodegui/react-nodegui";
import React from "react";
const dogImg = require("./dog");
export function StepOne() {
return (
<View style={containerStyle}>
<Text wordWrap={true}>
Edit App.tsx to make changes to this screen. Then come back to see your
changes. Changes should reflect live thanks to Hot Reloading. 🔥
</Text>
<Text>
{`
<p style="color: rgb(255,72,38);">
<center>
<img src="${dogImg}" alt="doggy" />
</center>
<center>You can even use <i><strong>Rich Html</strong></i> text like this if you want 😎.</center>
</p>
<hr />
`}
</Text>
</View>
);
}
const containerStyle = `
margin-horizontal: 20px;
padding-horizontal: 10px;
`;

@ -1,53 +0,0 @@
import { Text, View, Button, useEventHandler } from "@nodegui/react-nodegui";
import { QPushButtonSignals } from "@nodegui/nodegui";
import React from "react";
import open from "open";
export function StepTwo() {
const btnHandler = useEventHandler<QPushButtonSignals>(
{
clicked: () => open("https://react.nodegui.org").catch(console.log)
},
[]
);
return (
<View style={containerStyle}>
<Text style={textStyle} wordWrap={true}>
{`
<ol>
<li>
Open chrome and navigate to chrome://inspect. You should see a target below with your app.
</li>
<br/>
<li>
Next click on "Open dedicated DevTools for Node"
</li>
<br/>
<li>
On the dedicated devtools. Click on Source > Node > "Your node process"
</li>
</ol>
`}
</Text>
<Button
style={btnStyle}
on={btnHandler}
text={`Open React NodeGui docs`}
></Button>
</View>
);
}
const containerStyle = `
flex: 1;
justify-content: 'space-around';
`;
const textStyle = `
padding-right: 20px;
`;
const btnStyle = `
margin-horizontal: 20px;
height: 40px;
`;

@ -1,4 +1,4 @@
import { Renderer } from "@nodegui/react-nodegui";
import {Renderer} from "@nodegui/react-nodegui";
import React from "react";
import App from "./app";

@ -0,0 +1,40 @@
import {TextTranslateResponse} from "tencentcloud-sdk-nodejs/tencentcloud/services/tmt/v20180321/tmt_models";
const tencentcloud = require("tencentcloud-sdk-nodejs")
const TmtClient = tencentcloud.tmt.v20180321.Client;
const clientConfig = {
credential: {
secretId: process.env.REACT_APP_TENCENT_SECRET_ID,
secretKey: process.env.REACT_APP_TENCENT_SECRET_KEY,
},
region: process.env.REACT_APP_TENCENT_REGION,
profile: {
httpProfile: {
endpoint: process.env.REACT_APP_TENCENT_ENDPOINT,
},
},
};
const client = new TmtClient(clientConfig)
export default function tencentTmt(SourceText:string,Target:string){
return new Promise((resolve:(value:TextTranslateResponse)=>void, reject) => {
const params = {
SourceText,
"Source": "auto",
Target,
"ProjectId": 0
};
client.TextTranslate(params).then(
(res:TextTranslateResponse) => {
resolve(res)
},
(err: any) => {
reject(err)
}
)
})
}

@ -2,7 +2,7 @@ const path = require("path");
const webpack = require("webpack");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const Dotenv = require('dotenv-webpack')
module.exports = (env, argv) => {
const config = {
mode: "production",
@ -42,7 +42,10 @@ module.exports = (env, argv) => {
}
]
},
plugins: [new CleanWebpackPlugin()],
plugins: [new CleanWebpackPlugin(),
new Dotenv({
path: path.resolve(__dirname, '.env')
}),],
resolve: {
extensions: [".tsx", ".ts", ".js", ".jsx", ".json"]
}

Loading…
Cancel
Save