目录

前言 :为什么要写这篇文章?

一、superset是什么?

二、建议软件

三、安装步骤

1.安装docker

2.拉取源码

3.修改源码的dockerfile

4.创建superset容器

5.修改容器内的账号密码

6.修改Superset仪表盘(Dashboard)可以匿名访问(免登录)

7.发布dashboard并自定义url

四、二次开发(自定义图表)

1.创建一个简单的 Hello World viz 插件

2.运行

3.创建一个其他的图表(集成echarts)

4.创建一个百度地图插件(使用echarts+echarts for react)


前言为什么要写这篇文章

 我希望通过 Docker服务器安装 Superset以便独立构建 Superset 环境。

× 我不想使用 Docker Compose,因为是多容器部署,且服务器环境不便于进行二次开发

× 我不想使用 Pip 安装,因为这会导致缺失 supersetfrontend 目录,无法修改前端代码自定义 Superset图表内容

× 我不想使用 Dockerhub 上的镜像,因为官方 apache/superset镜像 缺失 supersetfrontend 目录内容,而 amancevice/superset镜像则是使用 pip 安装 Superset,都无法修改前端代码自定义 Superset图表内容

× Superset版本更新比较频繁,因此版本信息比较混乱。目前官方文档比较落后且存在错误,也没有针对特定版本进行说明和指导。


一、superset是什么?

Superset一个基于 Python Flask 和 Apache Superset数据可视化探索平台。它是由 Airbnb 在 2015 年发布的,并于 2017 年开源,现已成为 Apache 基金会孵化项目之一。

Superset 提供了一个交互式数据可视化界面支持从多种数据源获取探索数据,如 SQL 数据库、NoSQL 数据库搜索引擎等。用户可以通过 Superset 快速创建和共享数据分析可视化应用支持多种图表类型样式,包括折线图柱状图散点图地图等。

二、建议软件

建议使用 VScode 软件,并安装 Remote-SSH 插件远程连接您的服务器。此外,您可以安装 Docker 插件来方便地管理 Docker 容器镜像

三、安装步骤

1.安装docker

请参考​​​​​安装Docker详细步骤总结这里就不重复了。

2.拉取源码

创建并进入superset文件夹

mkdir superset && cd superset

初始化空的 Git 版本库

git init

github拉取源码

git clone https://github.com/apache/superset.git

或 国内可以通过gitee镜像拉取源码,并与远程仓库建立连接

git clone https://gitee.com/mirrors/Superset.git

进入Superset文件夹

cd Superset

切换到2.1.0版本

git checkout 2.1.0

3.修改源码dockerfile

该 Dockerfile 有三个部分,分别对应三个阶段

  1. 第一部分是 Node 阶段用于构建静态资源

  2. 第二部分是 Lean 阶段用于实际运行程序,并生成发布的 Docker 镜像

  3. 第三部分是 Dev 阶段用于开发环境。

我们需要dockerfile做出以下修改:(修改好的dockerfile在后面)

  1. 修改依赖python3.8.16版本和node16版本

  2. 在Lean阶段修改apt源,将它改为阿里

  3. 在Lean阶段,在运行环境中通过apt安装sudo

  4. 在Lean阶段,在运行环境中安装node16(二次开发需要node16和npm7或8版本)

  5. 在Lean阶段将只复制superset-frontend/package.json文件改成从node阶段复制整个superset-frontend文件夹运行环境

  6. 在Lean阶段修改pip源,将它改为阿里

  7. 在Lean阶段复制superset文件夹后,superset汉化,修改config.py(我直接在构建镜像阶段修改了,也可以在启动镜像手动修改),手动修改如下:

以下是我修改后的dockerfile

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

######################################################################
# Node stage to deal with static asset construction
######################################################################
ARG PY_VER=3.8.16
FROM node:16 AS superset-node

ARG NPM_BUILD_CMD="build"
ENV BUILD_CMD=${NPM_BUILD_CMD}
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true

# NPM ci first, as to NOT invalidate previous steps except for when package.json changes
RUN mkdir -p /app/superset-frontend

COPY ./docker/frontend-mem-nag.sh /
RUN /frontend-mem-nag.sh

WORKDIR /app/superset-frontend/

COPY superset-frontend/package*.json ./
# 若此npm源不好用删除npm config set registry https://registry.npmmirror.com && 
RUN npm config set registry https://registry.npmmirror.com && 
    npm ci

COPY ./superset-frontend .

# This seems to be the most expensive step
RUN npm run ${BUILD_CMD}

######################################################################
# Final lean image...
######################################################################
FROM python:${PY_VER} AS lean

ENV LANG=C.UTF-8 
    LC_ALL=C.UTF-8 
    FLASK_ENV=production 
    FLASK_APP="superset.app:create_app()" 
    PYTHONPATH="/app/pythonpath" 
    SUPERSET_HOME="/app/superset_home" 
    SUPERSET_PORT=8088

RUN sed -i 's/http://deb.debian.org/debian/http://mirrors.aliyun.com/debian/g' /etc/apt/sources.list 
    && sed -i 's/http://security.debian.org/debian-security/http://mirrors.aliyun.com/debian-security/g' /etc/apt/sources.list 
    && mkdir -p ${PYTHONPATH} 
    && useradd --user-group -d ${SUPERSET_HOME} -m --no-log-init --shell /bin/bash superset 
    && apt-get update -y 
    && apt-get upgrade -y 
    && apt-get install -y --no-install-recommends 
    build-essential 
    curl 
    default-libmysqlclient-dev 
    libsasl2-dev 
    libsasl2-modules-gssapi-mit 
    libpq-dev 
    libecpg-dev 
    sudo 
    && apt-get install -y curl dirmngr apt-transport-https lsb-release ca-certificates 
    && curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - 
    && apt-get install -y nodejs 
    && rm -rf /var/lib/apt/lists/* 
    && npm config set registry https://registry.npmmirror.com
# 若此npm源不好用删除行&& npm config set registry https://registry.npmmirror.com

COPY ./requirements/*.txt  /app/requirements/
COPY setup.py MANIFEST.in README.md /app/

# setup.py uses the version information in package.json
COPY ./superset-frontend /app/superset-frontend

RUN mkdir ~/.pip/ 
    && echo '[global]' > ~/.pip/pip.conf 
    && echo 'index-url = https://mirrors.aliyun.com/pypi/simple/' >> ~/.pip/pip.conf 
    && echo 'trusted-host = mirrors.aliyun.com' >> ~/.pip/pip.conf 
    && cd /app 
    && mkdir -p superset/static 
    && touch superset/static/version_info.json 
    && pip install --no-cache -r requirements/local.txt

COPY --from=superset-node /app/superset/static/assets /app/superset/static/assets

## Lastly, let's install superset itself
COPY superset /app/superset
RUN sed -i 's/BABEL_DEFAULT_LOCALE = "en"/BABEL_DEFAULT_LOCALE = "zh"/g' /app/superset/config.py

COPY setup.py MANIFEST.in README.md /app/
RUN cd /app 
    && chown -R superset:superset * 
    && pip install -e . 
    && flask fab babel-compile --target superset/translations

COPY ./docker/run-server.sh /usr/bin/

RUN chmod a+x /usr/bin/run-server.sh

WORKDIR /app

USER superset

HEALTHCHECK CMD curl -f "http://localhost:$SUPERSET_PORT/health"

EXPOSE ${SUPERSET_PORT}

CMD /usr/bin/run-server.sh

######################################################################
# Dev image...
######################################################################
FROM lean AS dev
ARG GECKODRIVER_VERSION=v0.32.0
ARG FIREFOX_VERSION=106.0.3

COPY ./requirements/*.txt ./docker/requirements-*.txt/ /app/requirements/

USER root

RUN apt-get update -y 
    && apt-get install -y --no-install-recommends 
    libnss3 
    libdbus-glib-1-2 
    libgtk-3-0 
    libx11-xcb1 
    libasound2 
    libxtst6 
    wget

# Install GeckoDriver WebDriver
RUN wget https://github.com/mozilla/geckodriver/releases/download/${GECKODRIVER_VERSION}/geckodriver-${GECKODRIVER_VERSION}-linux64.tar.gz -O /tmp/geckodriver.tar.gz && 
    tar xvfz /tmp/geckodriver.tar.gz -C /tmp && 
    mv /tmp/geckodriver /usr/local/bin/geckodriver && 
    rm /tmp/geckodriver.tar.gz

# Install Firefox
RUN wget https://download-installer.cdn.mozilla.net/pub/firefox/releases/${FIREFOX_VERSION}/linux-x86_64/en-US/firefox-${FIREFOX_VERSION}.tar.bz2 -O /opt/firefox.tar.bz2 && 
    tar xvf /opt/firefox.tar.bz2 -C /opt && 
    ln -s /opt/firefox/firefox /usr/local/bin/firefox

# Cache everything for dev purposes...
RUN cd /app 
    && pip install --no-cache -r requirements/docker.txt 
    && pip install --no-cache -r requirements/requirements-local.txt || true

RUN pip uninstall --yes babel && pip install babel 

USER superset

######################################################################
# CI image...
######################################################################
FROM lean AS ci

COPY --chown=superset ./docker/docker-bootstrap.sh /app/docker/
COPY --chown=superset ./docker/docker-init.sh /app/docker/
COPY --chown=superset ./docker/docker-ci.sh /app/docker/

RUN chmod a+x /app/docker/*.sh

CMD /app/docker/docker-ci.sh

RUN pybabel compile -d /app/superset/translations || true

保存Dockerfile,并在Dockerfile所在的目录执行时间较长)

docker build -t mysuperset:2.1.0 .

docker build这一步占用内存较高,请保证有16G可用内存。

执行成功后,我创建了名为mysuperset版本号为2.1.0的image。

4.创建superset容器

通过刚刚创建的mysuperset:2.1.0镜像创建容器,并将其8088端口(此端口号可在dockerfile中修改)映射主机的8090端口(选一个未被占用主机端口),设置参数SUPERSET_SECRET_KEY=123456,将其命名为superset

docker run -d -p 8090:8088 -p 9000:9000 -e "SUPERSET_SECRET_KEY=123456" --name superset mysuperset:2.1.0

初始化用户用户名admin密码admin

docker exec -it superset superset fab create-admin 
              --username admin 
              --firstname Superset 
              --lastname Admin 
              --email admin@superset.com 
              --password admin 

本地数据库迁移最新版本。

docker exec -it superset superset db upgrade

执行汉化

docker exec -it superset pybabel compile -d /app/superset/translations

初始化

docker exec -it superset superset init

5.修改容器内的账号密码

正在运行的superset容器中,以 root 用户身份启动一个新的交互式 Bash 会话

docker exec -u root -it superset /bin/bash

修改密码

passwd

输入并确认你想改成的密码,为了方便记忆可修改为root

修改superset用户密码

passwd superset

 输入并确认你想改成的密码,为了方便记忆可修改为superset

退出会话

exit

6.修改Superset仪表盘(Dashboard)可以匿名访问(免登录

这个方法百度搜到的其他方法不同,百度搜索结果中的方法是“修改public权限等同gamma”

主机ip:8090为你刚才启动的superset链接(localhost:8090,请修改localhost为你的主机ip)

登录Superset,账号密码均为刚才设置的:admin

点击右上角的 设置>角色列表

 选中Admin点击操作,复制角色

编辑刚刚复制的角色 

修改名称copyToPublic(名字随意)

权限删除所有带有write权限。在网页使用ctrl+F搜索write,点x删除

删除menu access on List Users权限

删除menu access on List Roles权限

点击保存这里一定要点保存

手动修改容器中/app/superset/config.py文件,将PUBLIC_ROLE_LIKE: Optional[str] = None的None改成”copyToPublic”,此处copyToPublic带有引号,请注意使用英文引号。(或可通过如下命令修改)

docker exec -it superset sed -i 's/PUBLIC_ROLE_LIKE: Optional[str] = None/PUBLIC_ROLE_LIKE: Optional[str] = "copyToPublic"/g' /app/superset/config.py

初始化supserset

docker exec -it superset superset init

此时可以不登录访问已创建的仪表盘(dashboard)、图表等了。

7.发布dashboard并自定义url

发布仪表盘(dashboard)后,可以通过修改看板属性中的SLUG等,生成url

以下 URL 参数用于修改仪表板的呈现方式:此处参考了官方文档

standalone:

show_filters:

expand_filters:

例如,在运行本地开发构建时,以下内容将禁用顶部导航栏并删除过滤器条:

http://localhost:8090/superset/dashboard/my-dashboard/?standalone=1&show_filters=0

四、二次开发(自定义图表)

superset已经有的图表无法满足我的需要,我想要在superset中集成更多的自定义的图表。

官方文献:Creating Visualization Plugins | Superset

参考文献1:https://www.youtube.com/watch?v=LDHFY9xTzls&ab_channel=Preset 

参考文献2:Building Custom Viz Plugins in Superset v2 (Updated for Monorepo) | Preset

1.创建一个简单的 Hello World viz 插件

正在运行的 superset器中,以 root 用户身份启动一个新的交互式 Bash 会话

docker exec -u root -it superset /bin/bash

修改npm源为淘宝

npm config set registry https://registry.npmmirror.com

全局范围内安装 Yo 命令行工具 

npm i -g yo

进入/app/superset-frontend/packages/generator-superset目录

cd /app/superset-frontend/packages/generator-superset

执行

npm i && npm link

切换用户superset

su superset

此时你的用户是 superset

为您的可视化插件创建一个新目录,目录名的前缀应该是 superset-pluginchart,然后运行 Yeoman 生成器

mkdir /tmp/superset-plugin-chart-hello-world
cd /tmp/superset-plugin-chart-hello-world
yo @superset-ui/superset

根据引导创建,运行结果如下所示

开始build执行

npm i --force
npm run build

进入/app/superset-frontend

cd /app/superset-frontend

向superset添加图表,执行链接到已创建的图表)

npm i -S /tmp/superset-plugin-chart-hello-world

我们可以看到在/app/superset-frontend/package.json文件中,已经添加了我自定义的图表。

 修改/app/superset-frontend/src/visualizations/presets/MainPreset.js文件,修改两处,一处引用,一处配置

引用:在此处加入行

import { SupersetPluginChartHelloWorld } from 'superset-plugin-chart-hello-world';

 配置:在此处插入行​​​​​​

new SupersetPluginChartHelloWorld().configure({
          key: 'ext-hello-world',
        }),

 请注意,在修改MainPreset.js文件时,不要添加无意义的回车和空格,会导致报错

2.运行

在/app/superset-frontend目录下

执行分为两种方式:

  1. dev临时的页面(可实时修改),此处建议使用dev,完成全部修改后再执行build
  2. 正式的页面

临时的页面在9000端口(React在保存时热重载和重新编译文件)

npm run dev-server

http://localhost:9000

 由于React保存时热重载和重新编译文件,因此需要跟踪所有项目文件。

若出现了文件观察器达到限制问题如下所示:可以重启superset容器。或修改docker的配置

<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:9000/
<i> [webpack-dev-server] Content not from webpack is served from '/app/static/assets' directory
<i> [webpack-dev-server] 404s will fallback to '/index.html'
10% building 0/1 entries 0/0 dependencies 0/0 modulesnode:internal/errors:478
    ErrorCaptureStackTrace(err);
    ^

Error: ENOSPC: System limit for number of file watchers reached, watch '/app'
    at FSWatcher.<computed> (node:internal/fs/watchers:244:19)
    at Object.watch (node:fs:2296:34)
    at createFsWatchInstance (/app/superset-frontend/node_modules/chokidar/lib/nodefs-handler.js:119:15)
    at setFsWatchListener (/app/superset-frontend/node_modules/chokidar/lib/nodefs-handler.js:166:15)
    at NodeFsHandler._watchWithNodeFs (/app/superset-frontend/node_modules/chokidar/lib/nodefs-handler.js:331:14)
    at NodeFsHandler._handleDir (/app/superset-frontend/node_modules/chokidar/lib/nodefs-handler.js:567:19)
    at NodeFsHandler._addToNodeFs (/app/superset-frontend/node_modules/chokidar/lib/nodefs-handler.js:617:27)
    at async /app/superset-frontend/node_modules/chokidar/index.js:451:21
    at async Promise.all (index 0)
Emitted 'error' event on FSWatcher instance at:
    at FSWatcher._handleError (/app/superset-frontend/node_modules/chokidar/index.js:647:10)
    at NodeFsHandler._addToNodeFs (/app/superset-frontend/node_modules/chokidar/lib/nodefs-handler.js:645:18)
    at async /app/superset-frontend/node_modules/chokidar/index.js:451:21
    at async Promise.all (index 0) {
  errno: -28,
  syscall: 'watch',
  code: 'ENOSPC',
  path: '/app',
  filename: '/app'
}

若运行正式版本,则执行

npm run build

执行正式页面需重启容器 

e.g. 打开页面实在是太卡了,我把整个docker容器都重启了。

systemctl restart docker

然后重启需要打开的容器。

http://localhost:8090

在创建图表时搜索:hello即可找到刚刚创建的图表

如果想要修改图标,可以替换/app/superset-frontend/node_modules/superset-plugin-chart-hello-world/src/images目录下的thumbnail.png文件。

3.创建一个其他的图表(集成echarts

同上述方法,通过yo,创建/tmp/superset-plugin-chart-test,创建一个名为superset-plugin-chart-test的插件。并在/app/superset-frontend目录下执行npm i -S /tmp/superset-plugin-chart-test,并修改/app/superset-frontend/src/visualizations/presets/MainPreset.js文件。(和1.2.中的方法相同)

在/tmp/superset-plugin-chart-test目录下安装echarts-for-react

npm i echarts-for-react --save

修改/tmp/superset-plugin-chart-test/src/SupersetPluginChartTest.tsx文件,改成

import ReactEcharts from "echarts-for-react"
import React from "react"

const SupersetPluginChartTest = () => {
  const getOption = () => {
    return {
      title: {
        text: "ECharts 入门示例",
      },
      tooltip: {},
      legend: {
        data: ["销量"],
      },
      xAxis: {
        data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
      },
      yAxis: {},
      series: [
        {
          name: "销量",
          type: "bar",
          data: [5, 20, 36, 10, 10, 20],
        },
      ],
    }
  }

  return <ReactEcharts option={getOption()} />
}

export default SupersetPluginChartTest;

在/tmp/superset-plugin-chart-test 目录下执行

npm run build

在/app/superset-frontend目录下

npm run dev-server

npm run build

如此,即可在superset中创建此chart,如图所示

4.创建一个百度地图插件(使用echarts+echarts for react

在已经通过npm i –save  …….安装好echartsecharts for react后。

同上述方法,通过yo,创建/tmp/superset-plugin-baidumap,创建一个名为superset-plugin-baidu-map的插件。并在/app/superset-frontend目录下执行npm i -S /tmp/superset-plugin-baidu-map,并修改/app/superset-frontend/src/visualizations/presets/MainPreset.js文件。(和1.2.中的方法相同)

修改/tmp/superset-plugin-chart-test/src/SupersetPluginChartTest.tsx文件,改成

import React from "react"
import ReactEcharts from "echarts-for-react"
import echarts from "echarts";
import 'echarts/extension/bmap/bmap';

const SupersetPluginChartBaiduMap = () => {
  const getOption = () => {
    return {
      bmap: {
        center: [104.114129, 37.550339],
        zoom: 5,
        roam: true
      },
      series: [
        {
          type: 'scatter',
          coordinateSystem: 'bmap',
          symbolSize: 20,
          data: [[104.114129, 37.550339]]
        }
      ]
    }
  }
  return (
      <ReactEcharts option={getOption()} echarts={echarts} />
  )
}

export default SupersetPluginChartBaiduMap;

再修改/app/superset/templates/tail_js_custom_extra.html文件

末尾加入

<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=YOUR_API_KEY"></script>

 ak为你申请百度地图api,请选择应用类型浏览器端。

在dashboard中,若想要删除百度地图左下角的图表,可以修改dashboard的css ,增加

/* 隐藏地图bmap左下角logo */
.anchorBL{
display:none;
}

5.通过我创建的baidu map插件了解如何添加控件传递数据

如果想像其他图表一样,添加控件,可以参考我自己创建的Superset百度地图插件

经度、纬度、机构名称地址指标、默认图标颜色为必填项。

地图页面(主页面:superset-plugin-chart-baidu-map/src/SupersetPluginChartBaiduMap.tsx

要修改的文件

1. 用于传输配置数据的文件

  1. 传输数据
    superset-plugin-chart-baidu-map/src/plugin/transformProps.ts
  2. interface接口,用于页面中使用数据
    superset-plugin-chart-baidu-map/src/types.ts
  3. 主页面使用数据
    superset-plugin-chart-baidu-map/src/SupersetPluginChartBaiduMap.tsx
  4. 修改测试文件(可改可不改)
    superset-plugin-chart-baidu-map/test/plugin/transformProps.test.ts

2. 用于传输query数据的文件

  1. 控件面板
    superset-plugin-chart-baidu-map/src/plugin/controlPanel.ts
  2. 建立query用于构建SQL语句构建结果可以在图表的检查查询中看到)
    superset-plugin-chart-baidu-map/src/plugin/buildQuery.ts

控件类别
AnnotationLayerControl: 注释图层控件
BoundsControl: 边界控件
CheckboxControl: 复选框控件
CollectionControl: 集合控件
ColorPickerControl: 颜色选择器控件
ColorSchemeControl: 颜色方案控件
DatasourceControl: 数据源控件
DateFilterControl: 日期过滤器控件
FixedOrMetricControl: 固定值或指标控件
HiddenControl: 隐藏控件
SelectAsyncControl: 异步选择控件
SelectControl: 选择控件
SliderControl: 滑块控件
SpatialControl: 空间控件
TextAreaControl: 多行文本控件
TextControl: 单行文本控件
TimeSeriesColumnControl: 时间序列列控件
ViewportControl: 视口控件
VizTypeControl: 可视化类型控件
MetricsControl: 指标控件
AdhocFilterControl: 自定义过滤器控件
FilterBoxItemControl: 过滤器框控件
DndColumnSelect: 数据列拖放选择控件
DndFilterSelect: 过滤器拖放选择控件
DndMetricSelect: 指标拖放选择控件

控件设置control panel :

superset-plugin-chart-baidu-map/src/plugin/controlPanel.ts

命名及使用规则

1. 规则1
superset-plugin-chart-baidu-map/src/plugin/controlPanel.ts中的“name”项、
superset-plugin-chart-baidu-map/src/plugin/buildQuery.ts
下划线分隔的小写字符串命名法,如:abcd_efgh

2. 规则2
superset-plugin-chart-baidu-map/src/plugin/transformProps.ts、
superset-plugin-chart-baidu-map/src/types.ts、
superset-plugin-chart-baidu-map/src/SupersetPluginChartBaiduMap.tsx
superset-plugin-chart-baidu-map/test/plugin/transformProps.test.ts
变量名需沿用1中的name并改成驼峰式,如:abcdEfgh。

翻译

此处以汉化为例:

在ts、js、tsx、jsx等文件生成界面上,如果需要在页面上汉化

import { t } from '@superset-ui/core';

在需要汉化文本的地方使用

t('text need to translate')

需要修改文件,自定义添加原文和对应汉语翻译
/app/superset/translations/zh/LC_MESSAGES/messages.po
重新执行编译

pybabel compile -d /app/superset/translations

echarts symbol图标用法

原文链接

例如:在图表中修改该symbol

symbol: 'image://data:image/gif;base64,xxxxxxxx'

xxxxxxxx处为图片的base64编码,可使用在线转换
此处建议转成gif图片后再转成base64编码svg图片的base64编码会导致图标显示

原文地址:https://blog.csdn.net/weixin_43219396/article/details/130271278

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_12855.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注