当前位置:首页 > 芯闻号 > 充电吧
[导读]Qt官方的Webview仅仅在Qtquick中支持安卓和ios,安卓的官方实现是调用安卓系统自带的浏览器API,但是Qt官方没有写js交互,于是研究了一通宵得出几个解决方案: 1.使用Qt官方的QM

Qt官方的Webview仅仅在Qtquick中支持安卓和ios,安卓的官方实现是调用安卓系统自带的浏览器API,但是Qt官方没有写js交互,于是研究了一通宵得出几个解决方案: 1.使用Qt官方的QML webview和HTML5的Websockt,在js中使用websockt,在qt qucik中使用websocket服务器,结果可行,可惜安卓很多版本不支持Websockt,于是乎3个小时过去了。

2.既然websockt行不通,就想了下socket.io库,能运行~可惜qt quick的Websockt服务器不支持socket.io的协议,于是乎 3小时又浪费了

3.使用HTML sockt或post,post要执行的函数,用QML的eval函数来执行字符串代码,于是乎用xhl来发送post数据,然后用C++建立TcpSocket来接收,但是我放弃了,解析HTML封包不划算,为了发送4个字符他会发一大堆HTML协议字符串…因为 socket由C++接收.websockt在 qt quick中,js post->sockt->qt quick 要跨3层交互才收的到数据,实在蛋疼…更重要的是还要避免意外情况,于是乎2小时又浪费了

4.最后花了5个小时,在国外看到一些开源代码,经过修改 实现了原生交互,依然是调用安卓的webview,内嵌到qt中 具体实现:c++ –> java  –> js

因为安卓的浏览器和java的交互是最稳定的

先生成Qt 安卓项目,先编译一次,在目录下新建android文件夹(QT中可以创建) 和 assets文件夹

把androud-build中的src目录下全部文件复制到android目录,打开eclipse,在QtActivity包下添加一个类:
下面代码是参考过来的:

package org.qtproject.qt5.android.bindings;

public interface NativeCalls {
    public void createNewWebView(int tag);
    public void removeWebView(int tag);
    public void moveWebView(int tag, int x, int y);
    public void resizeWebView(int tag, int w, int h);
    public void attachWebViewToMainLayout(int tag);
    public void loadUrlAtWebView(int tag, String url);
    public void loadHtmlAtWebView(int tag, String html); 
}

然后再添加一个类,用来继承webview,定制一些功能,比如开启js和弹窗等,并且添加JavaScript交互接口,接口函数在C++中定义,在JAVA中声明、

package org.qtproject.qt5.android.bindings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.webkit.*;
import android.content.Context;
import android.util.Log;

public class Cjavascript  {

    public Cjavascript  ( ) {


    }

    public  native int  testcall2();
    public  native void  testcall3();
    public  native void  testcall4();
    public  void testcall()
    {
        Log.i("testcalltestcalltestcalltestcall", "call:");
    }
}
package org.qtproject.qt5.android.bindings;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;


public class MyWebView extends WebView {

    public MyWebView(Context context) {

        super(context); 
        init();

        // TODO 自动生成的构造函数存根
    }



    @SuppressLint("SetJavaScriptEnabled")
    public void  init() {
        this.getSettings().setJavaScriptEnabled(true);
        this.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
            //否则不会执行js的Alert
        this.setWebChromeClient(new WebChromeClient()
            {   public boolean onJsAlert(WebView view, String url, String message,
                        JsResult result)
                {
                    //Auto-generated method stub
                    return super.onJsAlert(view, url, message, result);
                }

            });
        this.setWebViewClient(new WebViewClient());
        this.setBackgroundColor(Color.BLACK); 

        this.addJavascriptInterface(new Cjavascript(), "obj");
    }
}

最后就是修改Qt自己生成的QtActivity类,在作用域中添加以下代码即可:
下面的代码大部分参考别人的,少量修改。官方源码中都是使用RunOnUiThread,下面的是用handle进行多线程界面操作。

 //-------------WEBVIEW----------------
    protected ArrayListwebViewsList = new ArrayList();

    //-----------------------------------------------------------------------------
    protected MyWebView findWebViewByTag(int tag) {
        MyWebView webViewRes = null;
        for (int i = 0; i < webViewsList.size(); i++) {
            MyWebView wv = (MyWebView) webViewsList.get(i);
            if (((Integer)wv.getTag()).intValue() == tag) {
                webViewRes = wv;
                break;
            }
        }

        return webViewRes;
    }

    //-----------------------------------------------------------------------------
    @Override
    public void createNewWebView(int tag) {
        Message msg = new Message();
        msg.what = tag;

        createNewWebViewHandler.sendMessage(msg);
    }

    protected Handler createNewWebViewHandler = new Handler() {
        @SuppressLint("SetJavaScriptEnabled")
        @Override
        public void handleMessage(Message msg) {
            MyWebView webView = new MyWebView(QtActivity.this);

            webView.setTag(msg.what);
//              webView.getSettings().setJavaScriptEnabled(true);
//              webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
//              //否则不会执行js的Alert
//              webView.setWebChromeClient(new WebChromeClient()
//              {   public boolean onJsAlert(WebView view, String url, String message,
//                          JsResult result)
//                  {
//                      //Auto-generated method stub
//                      return super.onJsAlert(view, url, message, result);
//                  }
//
//              });
//              webView.setWebViewClient(new WebViewClient());
//              webView.setBackgroundColor(Color.BLACK); 

            webViewsList.add(webView); 

        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void removeWebView(int tag) {
        Message msg = new Message();
        msg.what = tag;

        removeWebViewHandler.sendMessage(msg);
    }

    protected Handler removeWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            MyWebView webView = findWebViewByTag(msg.what);
            if (webView != null) {
                webViewsList.remove(webView);
                FrameLayout mainLayout = (FrameLayout) findViewById(R.id.content);
                mainLayout.removeView(webView);
            }
        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void moveWebView(int tag, int x, int y) {
        Message msg = new Message();
        msg.what = tag;
        msg.arg1 = x;
        msg.arg2 = y;

        moveWebViewHandler.sendMessage(msg);
    }


    protected Handler moveWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            View viewToMove = null;
            viewToMove = findWebViewByTag(msg.what);

            if (viewToMove != null) {
                FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) viewToMove
                        .getLayoutParams();
                params.leftMargin = msg.arg1;
                params.topMargin = msg.arg2;
                viewToMove.setLayoutParams(params);
            }
        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void resizeWebView(int tag, int w, int h) {
        Message msg = new Message();
        msg.what = tag;
        msg.arg1 = w;
        msg.arg2 = h;

        resizeWebViewHandler.sendMessage(msg);
    }


    protected Handler resizeWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            View viewToResize = null;
            viewToResize = findWebViewByTag(msg.what);

            if (viewToResize != null) {
                FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) viewToResize
                        .getLayoutParams();
                params.width = msg.arg1;
                params.height = msg.arg2;
                viewToResize.setLayoutParams(params);
            }
        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void attachWebViewToMainLayout(int tag) {
        Message msg = new Message();
        msg.what = tag;

        attachWebViewToMainLayoutHandler.sendMessage(msg);
    }

    protected Handler attachWebViewToMainLayoutHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            View viewToAttach = null;
            viewToAttach = findWebViewByTag(msg.what);

            if (viewToAttach != null) {
                FrameLayout mainLayout = (FrameLayout) findViewById(R.id.content);
                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(100, 100);
                params.leftMargin = 0;
                params.topMargin = 0;

                mainLayout.addView(viewToAttach, params);
            }
        }
    };

    //-----------------------------------------------------------------------------
    @Override
    public void loadUrlAtWebView(int tag, String url) {
        Log.i("url", "curid:"+Thread.currentThread().getId());
        Message msg = new Message();
        msg.what = tag;
        msg.obj = url;


         loadUrlAtWebViewHandler.sendMessage(msg);
    }

    protected Handler loadUrlAtWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.i("url", "mainid:"+Thread.currentThread().getId());
            MyWebView webView = findWebViewByTag(msg.what);
            if (webView != null) {
                webView.loadUrl(msg.obj.toString());
                Log.i("url", msg.obj.toString());
            }
            //Log.i("TestNative*********", "call:");
            //TestNative  testNative  = new TestNative();
            //testNative.Test();
        }
    };

    //-----------------------------------------------------------------------------

    @Override
    public void loadHtmlAtWebView(int tag, String html) {
        Message msg = new Message();
        msg.what = tag;
        msg.obj = html;

        loadHtmlAtWebViewHandler.sendMessage(msg);
    }

    protected Handler loadHtmlAtWebViewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            MyWebView webView = findWebViewByTag(msg.what);
            if (webView != null) {
                webView.loadData(msg.obj.toString(), "text/html; charset=UTF-8", "UTF-8");

            }
        }
    };

C++层,注册JavaScript函数:

#ifndef JAVA_JAVASCTRIPTFUNC_H
#define JAVA_JAVASCTRIPTFUNC_H
#include#include#includeclass JAVA_javasctriptFunc
{
public:
    JAVA_javasctriptFunc();
    bool regfuc();
};

#endif // JAVA_JAVASCTRIPTFUNC_H

#include "java_javasctriptfunc.h"
int cout=0;
static jint testcall2(JNIEnv *env, jobject thiz)
{
    cout++;
   qInfo() << "MY C+++: testcall2"<<QThread::currentThreadId() ;

   return cout;
       // QMessageBox::information(NULL,"HEHE","33");
}
static void testcall3(JNIEnv *env, jobject thiz)
{
   qInfo() << "MY C+++: testcall3"<<QThread::currentThreadId()  ;
       // QMessageBox::information(NULL,"HEHE","33");
}
static void testcall4(JNIEnv *env, jobject thiz)
{
   qInfo() << "MY C+++: testcall4"<GetObjectClass(javaClass.object());
     qDebug() << "find ExtendsQtNative - " << clazz;
     bool result = false;
     if(clazz)
     {
         jint ret = env->RegisterNatives(clazz,
                                         methods,
                                         sizeof(methods) / sizeof(methods[0]));
         env->DeleteLocalRef(clazz);
         qDebug() << "RegisterNatives return - " << ret;
         result = ret >= 0;
     }
     if(env->ExceptionCheck()) env->ExceptionClear();
     return result;
 }

WEBVIEW的C++层实现:

#pragma once

#include#include#includeclass CAndroidNativeCallsSender
{
public:
    CAndroidNativeCallsSender();
    ~CAndroidNativeCallsSender();

    void createNewWebView( int id ) const;
    void removeWebView( int id ) const;
    void attachWebViewToMainLayout( int id ) const;
    void moveWebView( int id, int x, int y ) const;
    void resizeWebView( int id, int w, int h ) const;
    void loadUrlAtWebView( int id, QString const& url ) const;
    void loadHtmlAtWebView( int id, QString const& html ) const;

protected:
    jmethodID m_createNewWebViewMID;
    jmethodID m_removeWebViewMID;
    jmethodID m_attachWebViewToMainLayoutMID;
    jmethodID m_moveWebViewMID;
    jmethodID m_resizeWebViewMID;
    jmethodID m_loadUrlAtWebViewMID;
    jmethodID m_loadHtmlAtWebViewMID;

    jobject m_objectRef;
};
#include "AndroidNativeCallsSender.h"
#include//-----------------------------------------------------------------------------
CAndroidNativeCallsSender::CAndroidNativeCallsSender()
{
    QAndroidJniEnvironment jniEnv;
    QAndroidJniObject qObjAct = QAndroidJniObject::callStaticObjectMethod( "org/qtproject/qt5/android/QtNative",
                                                                           "activity",
                                                                           "()Landroid/app/Activity;" );
    jobject objAct = qObjAct.object();
    m_objectRef = jniEnv->NewGlobalRef( objAct );
    jclass cls = jniEnv->GetObjectClass( objAct );

    if( cls )
    {
        m_createNewWebViewMID = jniEnv->GetMethodID( cls, "createNewWebView", "(I)V" );
        m_removeWebViewMID = jniEnv->GetMethodID( cls, "removeWebView", "(I)V" );
        m_attachWebViewToMainLayoutMID = jniEnv->GetMethodID( cls, "attachWebViewToMainLayout", "(I)V" );
        m_moveWebViewMID = jniEnv->GetMethodID( cls, "moveWebView", "(III)V" );
        m_resizeWebViewMID = jniEnv->GetMethodID( cls, "resizeWebView", "(III)V" );
        m_loadUrlAtWebViewMID = jniEnv->GetMethodID( cls, "loadUrlAtWebView", "(ILjava/lang/String;)V" );
        m_loadHtmlAtWebViewMID = jniEnv->GetMethodID( cls, "loadHtmlAtWebView", "(ILjava/lang/String;)V" );
    }
}

//-----------------------------------------------------------------------------
CAndroidNativeCallsSender::~CAndroidNativeCallsSender()
{
    if( m_objectRef != NULL )
    {
        QAndroidJniEnvironment jniEnv;
        jniEnv->DeleteGlobalRef( m_objectRef );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::createNewWebView( int id ) const
{
    if( m_createNewWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jniEnv->CallVoidMethod( m_objectRef, m_createNewWebViewMID, id );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::removeWebView( int id ) const
{
    if( m_removeWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jniEnv->CallVoidMethod( m_objectRef, m_removeWebViewMID, id );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::attachWebViewToMainLayout( int id ) const
{
    if( m_attachWebViewToMainLayoutMID )
    {
        QAndroidJniEnvironment jniEnv;

        jniEnv->CallVoidMethod( m_objectRef, m_attachWebViewToMainLayoutMID, id );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::moveWebView( int id, int x, int y ) const
{
    if( m_moveWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jniEnv->CallVoidMethod( m_objectRef, m_moveWebViewMID, id, x, y );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::resizeWebView( int id, int w, int h ) const
{
    if( m_resizeWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;

        jniEnv->CallVoidMethod( m_objectRef, m_resizeWebViewMID, id, w, h );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::loadUrlAtWebView( int id, QString const& url ) const
{
    if( m_loadUrlAtWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jstring jurl = jniEnv->NewStringUTF( url.toUtf8().constData() );
        jniEnv->CallVoidMethod( m_objectRef,
                                m_loadUrlAtWebViewMID,
                                id,
                                jurl );
        jniEnv->DeleteLocalRef( jurl );
    }
}

//-----------------------------------------------------------------------------
void CAndroidNativeCallsSender::loadHtmlAtWebView( int id, QString const& html ) const
{
    if( m_loadUrlAtWebViewMID )
    {
        QAndroidJniEnvironment jniEnv;
        jstring jhtml = jniEnv->NewStringUTF( html.toUtf8().constData() );
        jniEnv->CallVoidMethod( m_objectRef,
                                m_loadHtmlAtWebViewMID,
                                id,
                                jhtml );
        jniEnv->DeleteLocalRef( jhtml );
    }
}
#pragma once

#include#include#include "AndroidNativeCallsSender.h"

class QtCustomAndroidWebView : public QWidget
{
    Q_OBJECT
public:
    explicit QtCustomAndroidWebView( QWidget *parent = 0 );
    ~QtCustomAndroidWebView();

    void loadURL( QString const& url ) const;
    void loadHtmlData( QString const& data ) const;

    void move( int x, int y );
    void move( QPoint const& p );
    void resize( int w, int h );

    int androidID() const { return m_androidID; }

protected:
    static int sm_tag;
    int m_androidID;
    CAndroidNativeCallsSender m_nativeSender;

    int generateNewTag();

signals:

public slots:
};
#include "QtCustomAndroidWebView.h"

//#include//-----------------------------------------------------------------------------
int QtCustomAndroidWebView::sm_tag = 0;

//-----------------------------------------------------------------------------
QtCustomAndroidWebView::QtCustomAndroidWebView( QWidget *parent )
    : QWidget( parent )
    , m_androidID( generateNewTag() )
{
//    if( parent && parent->layout() )
//        parent->layout()->addWidget( this );

    m_nativeSender.createNewWebView( androidID() );
    m_nativeSender.attachWebViewToMainLayout( androidID() );
}

//-----------------------------------------------------------------------------
QtCustomAndroidWebView::~QtCustomAndroidWebView()
{
    m_nativeSender.removeWebView( androidID() );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::loadURL( QString const& url ) const
{
    m_nativeSender.loadUrlAtWebView( androidID(), url );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::loadHtmlData( QString const& data ) const
{
    m_nativeSender.loadHtmlAtWebView( androidID(), data );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::move( int x, int y )
{
    QWidget::move( x, y );
    m_nativeSender.moveWebView( androidID(), x, y );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::move( QPoint const& p )
{
    QtCustomAndroidWebView::move( p.x(), p.y() );
}

//-----------------------------------------------------------------------------
void QtCustomAndroidWebView::resize( int w, int h )
{
    QWidget::resize( w, h );
    m_nativeSender.resizeWebView( androidID(), w, h );
}

//-----------------------------------------------------------------------------
int QtCustomAndroidWebView::generateNewTag()
{
    sm_tag++;
    return sm_tag;
}

测试代码

#include "mytest.h"
#include "QtCustomAndroidWebView.h"
#include#include#include#includeMyTest::MyTest(QWidget *parent) : QWidget(parent)
{
    vbox = new QVBoxLayout(this);

    btn = new QPushButton("call");
    btn->setText("CALL");
    edit = new QTextEdit();
      vbox->addWidget(btn);
      vbox->addWidget(edit);

//    webview = new QtCustomAndroidWebView();

//    webview->resize(this->width(),this->height());
//   webview->move(0,0);
//   webview->loadURL("http://www.baidu.com");
//   vbox->addWidget(webview);

 QScreen *screen = QApplication::primaryScreen();

   webview = new QtCustomAndroidWebView( );

     webview->resize(this->width(), 200);
     webview->loadURL("file:///android_asset/index.html");

    //注册js函数
     javajavasctriptFunc = new JAVA_javasctriptFunc();
     if(javajavasctriptFunc->regfuc()==false)
     {
                qDebug()<addWidget(webview);

      connect(btn,SIGNAL(clicked(bool)),this,SLOT(onclick()));
}


 void  MyTest::onclick()
 {
     int cout=0;
     while (1) {
         QThread::msleep(200);
         QString s;
         s.sprintf("javascript:test2(%d)",cout);
         cout++;
         webview->loadURL(s);
//      执行js脚本 返回值可参考Qt android src中WebView的实现     webview->loadURL("javascript:test()");
     }

 }
$(function(){

    $("button").click(function(){
    var  x=obj.testcall2();

         $("#list1").append("Inbox"+x+"");
        $('#list1').listview("refresh");

  });


});

如何调用JavaScript能获取返回值,可参考Qt的源码,其实没啥必要,只需要注册一个函数给js调用,让他把返回值弄进来就是

 m_webSettingsSetDisplayZoomControls = webSettings.getClass().getMethod("setDisplayZoomControls", boolean.class);
                        if (Build.VERSION.SDK_INT > 18) {
                            m_webViewEvaluateJavascript = m_webView.getClass().getMethod("evaluateJavascript",
                                                                                         String.class,
                                                                                         ValueCallback.class);
                        }
    public void runJavaScript(final String script, final long callbackId)
    {
        if (script == null)
            return;

        if (Build.VERSION.SDK_INT < 19 || m_webViewEvaluateJavascript == null)
            return;

        m_activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                try {
                    m_webViewEvaluateJavascript.invoke(m_webView, script, callbackId == -1 ? null :
                        new ValueCallback() {
                            @Override
                            public void onReceiveValue(String result) {
                                c_onRunJavaScriptResult(m_id, callbackId, result);
                            }
                        });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭