国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

簡(jiǎn)單5分鐘,將lowcode低代碼融入到你的中后臺(tái)管理系統(tǒng)

這篇具有很好參考價(jià)值的文章主要介紹了簡(jiǎn)單5分鐘,將lowcode低代碼融入到你的中后臺(tái)管理系統(tǒng)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

背景 (注: 2022.08.02 已更新最新官方版本)

你是否在做中后臺(tái)項(xiàng)目中經(jīng)常要重復(fù)做 crud 的業(yè)務(wù)邏輯,花費(fèi)大量時(shí)間還時(shí)常有 bug 發(fā)生,但是現(xiàn)在只要幾分鐘就能讓你快速連通前后端,拖拉拽實(shí)現(xiàn)后臺(tái)業(yè)務(wù)邏輯。你就問香不香!

關(guān)于

?? ?? ?? 基于 amis-editor(React + TS),通過封裝 json 數(shù)據(jù)上報(bào)、配置、自定義組件等,實(shí)現(xiàn)低代碼管理后臺(tái)實(shí)時(shí)更新,無需手動(dòng)寫 json 配置。如果你要在 Vue 中使用當(dāng)然也可以。

?? 簡(jiǎn)單一句話: 你不用敲代碼了!!

??? 覺得不錯(cuò)點(diǎn)個(gè) star 再走 !???

簡(jiǎn)單5分鐘,將lowcode低代碼融入到你的中后臺(tái)管理系統(tǒng)

在原框架上實(shí)現(xiàn)了哪些功能

  1. 支持 url 路由跳轉(zhuǎn)對(duì)應(yīng)的配置頁面
  2. 支持歷史記錄修改
  3. 支持預(yù)覽
  4. 支持重置
  5. 支持配置更新前端 lowcode 頁面(不用敲代碼嘍!?。。?/li>
  6. 通過路由及項(xiàng)目名配置查詢
  7. 支持切換環(huán)境
  8. 編輯器打包 (關(guān)閉 sourcemap 和 node 內(nèi)存溢出問題處理, 15M 體積)
  9. 新增 crud 模板
  10. 新增自定義 css 樣式模板
  11. 自定義組件示例(已內(nèi)置: 自定義標(biāo)題組件),已解決 remark、類型錯(cuò)誤導(dǎo)致無法渲染自定義組件的問題

如何使用

  npm i           //安裝依賴
  npm run start   //通過devserve啟動(dòng)前端頁面
  npm run server  //啟動(dòng)node服務(wù),默認(rèn)3001端口
  npm run build   //打包(某些情況可能會(huì)存在內(nèi)存溢出問題)

注意

1. 本地調(diào)試請(qǐng)?jiān)?server 文件夾下定義好文件名,本地調(diào)用通過文件名對(duì)應(yīng)路由名。如果需要數(shù)據(jù)庫連接,請(qǐng)定義好項(xiàng)目名和路由名等字段用于查詢。json 配置在原來基礎(chǔ)上,已經(jīng)做了一個(gè)包裹, 核心數(shù)據(jù)配置在 json 屬性內(nèi),為了方便定位以及后期維護(hù)擴(kuò)展。

2. 在編輯中極有可能遇到點(diǎn)錯(cuò)導(dǎo)致頁面丟失問題,可以做個(gè)發(fā)布的版本備份功能

{
  "json": {
    "type": "page",
    "title": "Hello world",
    "body": [
    ]
  },
  "routeName": "test2.json",
  "itemName": "cms2"
}

核心

//src/App.tsx
import * as React from "react";
import { Editor } from "amis-editor";
import "./App.css";
import axios from "axios";
import crudTpl from "./tpl/crud.json"; //json文件默認(rèn)可以在src目錄下導(dǎo)入
import { proxy } from "ajax-hook";  //攔截amis內(nèi)部ajax請(qǐng)求
import { SchemaObject } from "amis/lib/Schema"; //json數(shù)據(jù)類型
import { MyRendererPlugin } from "./MyRendererPlugin";
import { registerEditorPlugin } from 'amis-editor';

registerEditorPlugin(MyRendererPlugin); //自定義組件

interface StateType {
  json: any;
  routeName: string;
  itemName: string;
  preview: boolean;
  historyList: Object[];
  step: number;
  maxHistoryNum: number;
  baseURL: string;
  isCustomStyle: boolean
  linkDOM: HTMLElement | null
}

type InputType = React.RefObject<HTMLInputElement>

class App extends React.Component<any, StateType> {
  baseURLRef: InputType = React.createRef()
  itemNameRef: InputType = React.createRef()
  routeNameRef: InputType = React.createRef()

  constructor(props: any) {
    super(props);
    this.state = {
      json: {},
      routeName: window.localStorage.getItem("lowcode_routeName") || "test1", //test1對(duì)應(yīng)server文件夾下的json的文件名(本地調(diào)試)
      itemName: window.localStorage.getItem("lowcode_itemName") || "cms2",
      preview: false,
      historyList: [],
      step: 0,
      maxHistoryNum: 10,
      baseURL: window.localStorage.getItem("baseURL") || "http://localhost:3001", //正式開發(fā)環(huán)境請(qǐng)自行修改
      isCustomStyle: window.localStorage.getItem("lowcode_style") === 'true' ? true : false,
      linkDOM: null,
    };
  }
  componentDidMount() {
    //攔截處理
    proxy({
      onRequest: (config, handler) => {
        // config.headers = headers;  在這里處理通用請(qǐng)求頭
        config.url = this.state.baseURL + config.url;
        console.log("config", config);
        handler.next(config);
      },
      onError: (err, handler) => {
        console.log(err.type);
        handler.next(err);
      },
      onResponse: (response, handler) => {
        console.log(response.response);
        handler.next(response);
      },
    });

    //獲取url query
    this.checkQuery();
    setTimeout(() => {
      this.getJSON();
    }, 0);
  }

  /**
   * 通過接口獲取json對(duì)象
   */
  getJSON = () => {
    let {
      routeName,
      itemName,
    } = this.state;

    if (!routeName || !itemName) {
      alert("請(qǐng)傳入必要參數(shù)");
      return;
    }
    //這里要請(qǐng)求對(duì)應(yīng)的路由數(shù)據(jù)
    axios
      .post("/api/getJSON", {
        routeName: this.state.routeName,
        itemName: this.state.itemName,
      })
      .then((res) => {
        if (res.data.success === false) {
          alert(res.data.msg);
          return;
        }

        let obj = res.data;
        this.clearJSON();
        let newObj = this.changeBaseURLtoDomain(obj);

        this.setState(
          {
            json: newObj,
            historyList: [...this.state.historyList, newObj],
          },
          () => {
            console.log("獲取到最新的JSON", this.state.json);
          }
        );
      })
      .catch((e) => {
        alert("獲取后端json失敗" + JSON.stringify(e));
      });
  };

  /**
  * 通過接口保存json對(duì)象
  */
  sendJSON = () => {
    let {
      routeName,
      itemName,
    } = this.state;
    console.log(this.state.json)
    if (!routeName || !itemName) {
      alert("請(qǐng)傳入必要參數(shù)");
      return;
    }
    let obj = this.chengeDomaintoBaseURL(this.state.json);

    axios
      .post(
        "/api/setJSON",
        {
          json: obj,
          routeName: this.state.routeName,
          itemName: this.state.itemName,
        },
        {
          headers: {
            "Content-Type": "application/json",
          },
        }
      )
      .then((res) => {
        if (res.data.success === false) {
          alert(res.data.msg);
          return;
        }

        if (res && res.data && res.data.json) {
          alert("配置成功");
          let obj = res.data.json;
          this.setState({
            json: obj,
          });
        }
      })
      .catch((e) => {
        alert("存入配置失敗" + JSON.stringify(e));
      });
  };

  //監(jiān)聽lowcode的json改變
  handleChange = (e: any) => {
    console.log("更新了");
    this.setState(
      {
        json: e,
        historyList: [...this.state.historyList, e],
        step: this.state.step + 1,
      },
      () => {
        let { historyList, maxHistoryNum } = this.state;
        if (historyList.length > maxHistoryNum) {
          let limitObj = [...historyList].splice(-maxHistoryNum);
          this.setState({
            historyList: limitObj,
            step: this.state.step - 1,
          });
        }
        console.log("change", this.state.historyList);
      }
    );
  };

  //獲取query
  checkQuery = () => {
    let itemName = this.getQueryString("itemName");
    let routeName = this.getQueryString("routeName");
    if (itemName && routeName) {
      this.setState({
        itemName,
        routeName,
      });
    }
  };

  // 獲取查詢字符串
  getQueryString = (name: string) => {
    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
    var r = window.location.search.substr(1).match(reg);
    if (r != null) {
      return unescape(r[2]);
    } else {
      return null;
    }
  };

  //監(jiān)聽項(xiàng)目名輸入
  inputItemName = () => {
    if (!this.itemNameRef.current) return
    let val = this.itemNameRef.current.value as string
    this.setState({
      itemName: val,
    });
    window.localStorage.setItem("lowcode_itemName", val)
  };
  //監(jiān)聽路由輸入
  inputRouteName = () => {
    if (!this.routeNameRef.current) return
    let val = this.routeNameRef.current.value as string
    this.setState({
      routeName: val,
    });
    window.localStorage.setItem("lowcode_routeName", val)
  };
  //根路徑
  inputUrlName = () => {
    if (!this.baseURLRef.current) return
    let val = this.baseURLRef.current.value as string
    this.setState(
      {
        baseURL: val,
      },
      () => {
        window.localStorage.setItem("baseURL", this.state.baseURL);
      }
    );
  };

  //開始預(yù)覽
  startPreview = () => {
    this.setState({
      preview: !this.state.preview,
    });
  };
  //重置
  clearJSON = () => {
    this.setState({
      json: {},
    });
  };

  //上一步
  backHistoryJSON = () => {
    let { step, historyList } = this.state;
    if (step - 1 >= 0) {
      this.setState(
        {
          step: step - 1,
        },
        () => {
          this.setState({
            json: historyList[this.state.step],
          });
        }
      );
    } else {
      alert("您當(dāng)前沒有歷史記錄");
    }
  };
  //下一步
  goHistoryJSON = () => {
    let { step, historyList } = this.state;
    let curStep = historyList.length - 1;
    if (step < curStep) {
      this.setState(
        {
          step: step + 1,
        },
        () => {
          this.setState({
            json: historyList[this.state.step],
          });
        }
      );
    } else {
      alert("已經(jīng)是最新!");
    }
  };

  //設(shè)置自定義樣式
  setStyles = () => {
    this.setState({
      isCustomStyle: !this.state.isCustomStyle
    }, () => {
      if (this.state.isCustomStyle) {
        console.log("head", this.state.isCustomStyle);

        var url = "./styles/index.css";
        var link = document.createElement("link");
        link.setAttribute("rel", "stylesheet");
        link.setAttribute("type", "text/css");
        link.setAttribute("href", url);
        this.setState({
          linkDOM: link,
        });
        document.getElementsByTagName("head")[0].appendChild(link);
        window.localStorage.setItem("lowcode_style", 'true');
      } else {
        let head = document.getElementsByTagName("head");
        console.log("head", this.state.isCustomStyle);

        if (head && head[0] && this.state.linkDOM) {
          head[0].removeChild(this.state.linkDOM);
        }

        window.localStorage.setItem("lowcode_style", "false");
      }
    })
  };
  //crud模板
  setTpl = () => {
    let obj = this.changeBaseURLtoDomain(crudTpl) as SchemaObject
    this.setState({
      json: obj
    })
    alert("模板生成成功");
  };

  /**
   * 轉(zhuǎn)為domain, 注: 這里內(nèi)部是無法攔截axios的請(qǐng)求,所以這里直接對(duì)序列化的字符串做替換
   * 但是這種做法存在很容易出錯(cuò),所以我們直接攔截ajax請(qǐng)求。
   */
  changeBaseURLtoDomain = (obj: any) => {
    return obj
    // let { baseURL } = this.state;
    // if (!baseURL) return;
    // let str = JSON.stringify(obj);
    // let res = str.replace(/\$\{baseURL\}/g, baseURL);
    // return JSON.parse(res);
  };
  //轉(zhuǎn)為${baseURL}
  chengeDomaintoBaseURL = (obj: any) => {
    return obj
    // let { baseURL } = this.state;
    // if (!baseURL) return;
    // let str = JSON.stringify(obj);
    // let urlReg = new RegExp(baseURL, "g");
    // let res = str.replace(urlReg, "${baseURL}");
    // return JSON.parse(res);
  };

  render() {
    return (
      <>
        <div className="tabbar">
          <div>
            <span className="ml20">項(xiàng)目名:</span>
            <input
              type="text"
              ref={this.itemNameRef}
              className="input-info"
              placeholder={this.state.itemName}
              onChange={() => this.inputItemName()}
            />
            <span className="ml20">路由名:</span>
            <input
              type="text"
              ref={this.routeNameRef}
              className="input-info"
              placeholder={this.state.routeName}
              onChange={() => this.inputRouteName()}
            />
            <span className="ml20">設(shè)置baseURL:</span>
            <input
              type="text"
              ref={this.baseURLRef}
              placeholder={this.state.baseURL}
              onChange={() => this.inputUrlName()}
            />
            <button className="send-btn" onClick={this.getJSON}>
              獲取頁面
            </button>
          </div>
          <div>
            <button className="send-btn" onClick={this.setStyles}>
              {this.state.isCustomStyle ? '默認(rèn)樣式' : '自定義樣式'}
            </button>
            <button className="send-btn" onClick={this.setTpl}>
              crud模板
            </button>
            <button className="send-btn" onClick={this.backHistoryJSON}>
              上一步
            </button>
            <button className="send-btn" onClick={this.goHistoryJSON}>
              下一步
            </button>
            <button className="send-btn" onClick={this.clearJSON}>
              重置
            </button>
            <button className="send-btn" onClick={this.startPreview}>
              {this.state.preview ? "編輯" : "預(yù)覽"}
            </button>
            <button className="send-btn" onClick={this.sendJSON}>
              點(diǎn)擊配置生效
            </button>
          </div>
        </div>
        <Editor
          value={this.state.json}
          onChange={this.handleChange}
          preview={this.state.preview}
        />
      </>
    );
  }
}

export default App;

調(diào)整: 在編輯器中你無法攔截到內(nèi)部 amis 的 axios 請(qǐng)求實(shí)例,所以在原來的處理中域名是直接 json 解析,不方便處理,現(xiàn)在通過 ajax-hooks 庫直接攔截 ajax 請(qǐng)求,可以根據(jù)業(yè)務(wù)配置你的請(qǐng)求頭、域名等。

 npm i ajax-hook
import { proxy } from "ajax-hook";

//攔截處理
proxy({
	onRequest: (config, handler) => {
		// config.headers = headers;  在這里處理通用請(qǐng)求頭
		config.url = this.state.baseURL + config.url; //處理url
		handler.next(config);
	},
	onError: (err, handler) => {
		console.log(err.type);
		handler.next(err);
	},
	onResponse: (response, handler) => {
		console.log(response.response);
		handler.next(response);
	},
});

后端服務(wù)

//server/app.js  用于調(diào)試服務(wù)端
const http = require("http");
const fs = require("fs");
const path = require("path");

/**
 * 失敗數(shù)據(jù)模型
 * @param {*} msg 消息
 */
function errModel(msg) {
	let obj = {
		success: false,
		msg,
	};
	return JSON.stringify(obj);
}

http
	.createServer(function (req, res) {
		res.setHeader("Access-Control-Allow-Origin", "*");
		res.setHeader("Access-Control-Allow-Headers", "Content-Type");
		res.setHeader("Content-Type", "application/json;");
		res.setHeader(
			"Access-Control-Allow-Methods",
			"DELETE,PUT,POST,GET,OPTIONS"
		);
		console.log(req.url);
		console.log(req.method);
		if (req.method == "OPTIONS") {
			res.writeHead(200, {
				"Content-Type": "text/plain",
				"Access-Control-Allow-Origin": "*",
				"Access-Control-Allow-Headers":
					"Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild, sessionToken",
				"Access-Control-Allow-Methods": "PUT, POST, GET, DELETE, OPTIONS",
			});
			res.end("");
		}

		if (req.method === "POST" && req.url === "/api/setJSON") {
			let item = "";
			// 讀取每次發(fā)送的數(shù)據(jù)
			req.on("data", function (chunk) {
				item += chunk.toString();
			});
			// 數(shù)據(jù)發(fā)送完成
			req.on("end", function () {
				let items = JSON.parse(item);
				if (items.routeName && items.itemName) {
					let file = path.join(__dirname, `${items.routeName}.json`);
					// json文件需要存入路徑
					fs.writeFileSync(file, item);
					//將數(shù)據(jù)返回到客戶端
					res.write(item);
					res.end();
				} else {
					res.write(errModel("文件配置失敗, 檢查路由或項(xiàng)目名是否正確"));
					res.end();
				}
			});
		}

		//本地模擬直接用client-admin.json
		if (req.method === "POST" && req.url === "/api/getJSON") {
			let item = "";
			// 讀取每次發(fā)送的數(shù)據(jù)
			req.on("data", function (chunk) {
				item += chunk.toString();
			});
			// 數(shù)據(jù)發(fā)送完成
			req.on("end", function () {
				let items = JSON.parse(item);

				if (items.routeName && items.itemName) {
					let file = path.join(__dirname, `${items.routeName}.json`);

					fs.readFile(file, "utf-8", function (err, data) {
						if (err) {
							console.log(err);
							res.write(errModel("請(qǐng)檢查路由是否正確"));
							res.end();
						} else {
							let obj = JSON.parse(data);
							res.write(JSON.stringify(obj.json));
							res.end();
						}
					});
				} else {
					res.write(errModel("請(qǐng)檢查路由或項(xiàng)目名是否正確"));
					res.end();
				}
			});
		}
	})
	.listen(3001); // 監(jiān)聽的端口

如何在 Vue 的前端項(xiàng)目中使用 ?

1. 在靜態(tài)目錄 public 中的 index.html 引入對(duì)應(yīng)的 sdk,sdk 官網(wǎng)有可以自行下載

  <link rel="stylesheet" href="./lowcode/amis/antd.css" />
  <link rel="stylesheet" href="./lowcode/amis/iconfont.css" />
  <script src="./lowcode/amis/sdk.js"></script>

2. 在路由允許的情況下調(diào)用封裝的方法,即可渲染 lowcode 頁面

import Vue from "vue";
import defaultConfig from "./config";
import axios from "axios";

var timer = null;

let defaultOptions = {
	method: "local", // 'http' | 'local' 通過接口返回或者本地靜態(tài)文件夾獲取
	routeName: "", //輸入路由名(必填)
	itemName: "", //項(xiàng)目名(必填)
};
let newOptions; //修改后的配置
/**
 * 在路由允許的情況下調(diào)用可生成對(duì)應(yīng)lowcode頁面
 * @param {DOM} DOM
 * @param {Object} options
 */
export const getLowcodePage = (DOM, options = {}) => {
	newOptions = Object.assign(defaultOptions, options);
	let { routeName } = newOptions;
	if (!DOM || !routeName) {
		throw new Error("DOM or routeName is no exist");
	}

	//handle first render error
	const check = (routeName) => {
		let dom = document.querySelector(DOM);
		if (dom) {
			getJsonFs(routeName);
			if (!timer) {
				clearTimeout(timer);
			}
		} else {
			timer = setTimeout(() => {
				check(routeName);
			}, 0);
		}
	};

	//get json
	const getJsonFs = (routeName) => {
		if (newOptions.method === "local") {
			Vue.http
				.get(`lowcode/pages/${routeName}.json`, {}, { emulateJSON: true })
				.then((res) => {
					let obj = JSON.parse(res.bodyText);
					if (obj) {
						startAmis(obj);
					}
				})
				.catch((error) => {
					console.log("error", error);
				});
		}

		if (newOptions.method === "http") {
			//正式項(xiàng)目需要通過post請(qǐng)求傳入對(duì)象{routeName, itemName}
			//目前調(diào)試使用,注意某些跨域情況在vue.config.js中做跨域代理
			axios
				.post(
					"/api/getJSON",
					{
						routeName: options.routeName,
						itemName: options.itemName,
					},
					{
						headers: {
							"Content-Type": "application/json",
						},
					}
				)
				.then((res) => {
					let { data } = res;
					startAmis(data);
					console.log("http", data);
				})
				.catch((e) => {
					alert("獲取后端json失敗" + JSON.stringify(e));
				});
		}
	};

	//amis render
	const startAmis = (jsonObj) => {
		console.log("jsonObj", jsonObj);
		let amis = window.amisRequire("amis/embed");
		amis.embed(
			DOM,
			jsonObj,
			{
				data: {
					baseUrl: process.env.VUE_APP_API_BASE_URL,
				},
			},
			defaultConfig
		);
	};

	//entrance
	check(routeName);
};

3. 做跨域代理

  //vue.config.js
  devServer: {
    proxy: {
      //測(cè)試lowcode使用
      '/api': {
        target: 'http://localhost:3001',
        changeOrigin: true,
      },
    }
  },

4. 開始調(diào)用方法

<template>
  <div id='main-lowcode'>
    <div id="content-lowcode">
    </div>
  </div>

</template>

<script>
import { getLowcodePage } from '@/lowcode/index'

export default {
  data() {
    return {}
  },
  created() {},
  mounted() {
    // 獲取lowcode頁面
    getLowcodePage('#content-lowcode', {
      method: 'http', //'http'代表接口請(qǐng)求,注意如果是'local',請(qǐng)?jiān)趐ublic文件夾中放入json配置文件,即可本地獲取json頁面
      routeName: 'client-admin',
      itemName: 'cms2'
    })
  }
}
</script>

<style lang="less" scoped>

</style>

總結(jié)

實(shí)現(xiàn)以上基本能快速將中后臺(tái)系統(tǒng)集成進(jìn)低代碼頁面, 甚至單獨(dú)搭建一個(gè)低代碼管理后臺(tái)。 可謂是 crud 的解決辦法的神器。


問題 1: 如果在集成中的樣式需要做到統(tǒng)一?

可以在 amis 包的 amis.css 修改,建議根據(jù)原有中后臺(tái)系統(tǒng)配色修改,獨(dú)立引入 html。在編輯器中針對(duì)不同的中后臺(tái)項(xiàng)目,已經(jīng)封裝了可以通過按鈕預(yù)覽對(duì)應(yīng)的樣式的頁面,在/public/styles 可以配置修改。

問題 2: 如何自定義組件?

如果存在定制化的組件,也是可以通過自定義組件的方式引入,在 src/customComponents 里面已經(jīng)定義了一個(gè)示例,后期會(huì)新增更多自定義組件。。

問題 3: 如何處理權(quán)限?

可以通過 JSON 的解析,找到對(duì)應(yīng)的 disabled 字段,做對(duì)應(yīng)的修改即可

問題 4: 哪里找到大量的模板?

https://aisuda.bce.baidu.com/amis/examples/index

問題 5:真正如何托拉拽實(shí)現(xiàn),前端不用敲代碼!

在實(shí)踐中不能敲代碼,那么真正用編輯器實(shí)現(xiàn)一個(gè) crud 的功能,會(huì)遇到一些坑,如對(duì)應(yīng)的返回的數(shù)據(jù)格式可以有適配器轉(zhuǎn)換,查詢功能和實(shí)際列表展示,一定要注意映射字段的處理。在批量處理中一定要后端必須傳入 id。列表中的一些字段其實(shí)也可以用映射,按需展示,修改等。文章來源地址http://www.zghlxwxcb.cn/news/detail-402468.html

到了這里,關(guān)于簡(jiǎn)單5分鐘,將lowcode低代碼融入到你的中后臺(tái)管理系統(tǒng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包