2025-07-02 21:26:19 +08:00
|
|
|
|
use anyhow::Result;
|
|
|
|
|
|
use image::EncodableLayout;
|
2025-06-28 23:53:40 +08:00
|
|
|
|
#[allow(unused_imports)]
|
|
|
|
|
|
use tracing::{error, info, warn};
|
2025-06-28 16:43:09 +08:00
|
|
|
|
|
2025-07-02 21:26:19 +08:00
|
|
|
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
|
|
|
|
|
pub struct SearchResultItem {
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
code: String,
|
|
|
|
|
|
has_device: String,
|
|
|
|
|
|
img_urls: Vec<String>,
|
|
|
|
|
|
}
|
|
|
|
|
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
|
|
|
|
|
pub struct FetchResultItem {
|
|
|
|
|
|
pub imgs: Vec<iced::widget::image::Handle>,
|
|
|
|
|
|
pub name: String,
|
|
|
|
|
|
pub model:String,
|
|
|
|
|
|
pub code :String,
|
|
|
|
|
|
pub model_id: String,
|
|
|
|
|
|
pub datasheet:String,
|
|
|
|
|
|
}
|
|
|
|
|
|
/// 访问一次api,得到需要的部分数据
|
|
|
|
|
|
pub async fn search_keyword(
|
|
|
|
|
|
keyword: String,
|
|
|
|
|
|
cur_page: u32,
|
|
|
|
|
|
page_size: u32,
|
|
|
|
|
|
) -> Result<Vec<SearchResultItem>> {
|
|
|
|
|
|
let mut form_maps = std::collections::HashMap::new();
|
|
|
|
|
|
form_maps.insert("keyword", keyword);
|
|
|
|
|
|
let cur_page = format!("{cur_page}");
|
|
|
|
|
|
let page_size = format!("{page_size}");
|
|
|
|
|
|
form_maps.insert("curPage", cur_page);
|
|
|
|
|
|
form_maps.insert("pageSize", page_size);
|
|
|
|
|
|
let resp = reqwest::Client::new()
|
|
|
|
|
|
.post("https://pro.lceda.cn/api/eda/product/search")
|
|
|
|
|
|
.form(&form_maps)
|
|
|
|
|
|
.send()
|
|
|
|
|
|
.await?;
|
|
|
|
|
|
let text = resp.text().await?;
|
|
|
|
|
|
let j: KeywordSearchRoot = serde_json::from_str(&text)?;
|
|
|
|
|
|
let mut r = Vec::new();
|
|
|
|
|
|
for i in j.result.productList.iter() {
|
|
|
|
|
|
if let Some(has) = i.hasDevice.clone() {
|
|
|
|
|
|
let mut imgs = Vec::new();
|
|
|
|
|
|
if let Some(s) = i.image.clone() {
|
|
|
|
|
|
for img in s.imgs() {
|
|
|
|
|
|
imgs.push(img.clone());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let ii = SearchResultItem {
|
|
|
|
|
|
name: i.model.clone(),
|
|
|
|
|
|
code:i.code.clone(),
|
|
|
|
|
|
has_device: has,
|
|
|
|
|
|
img_urls: imgs,
|
|
|
|
|
|
};
|
|
|
|
|
|
r.push(ii);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Ok(r)
|
|
|
|
|
|
}
|
|
|
|
|
|
pub async fn download_step(id: String) -> Result<String> {
|
|
|
|
|
|
let url = format!("https://modules.lceda.cn/qAxj6KHrDKw4blvCG8QJPs7Y/{id}");
|
|
|
|
|
|
let url = url.as_str();
|
|
|
|
|
|
let resp = reqwest::get(url).await?;
|
|
|
|
|
|
let text = resp.text().await?;
|
|
|
|
|
|
Ok(text)
|
|
|
|
|
|
}
|
|
|
|
|
|
pub async fn fetch_item(item: SearchResultItem) -> Result<FetchResultItem> {
|
|
|
|
|
|
let h = search_has_device(&item.has_device).await?;
|
|
|
|
|
|
let mut has_device = String::new();
|
|
|
|
|
|
let v: serde_json::Value = serde_json::from_str(h.as_str())?;
|
|
|
|
|
|
let id = v["result"][0]["attributes"]["3D Model"].clone();
|
|
|
|
|
|
if let serde_json::Value::String(id) = id {
|
|
|
|
|
|
has_device = id.clone();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut title = String::new();
|
|
|
|
|
|
let mut footprint = String::new();
|
|
|
|
|
|
let mut datasheet = String::new();
|
|
|
|
|
|
|
|
|
|
|
|
if let serde_json::Value::String(t) = v["result"][0]["title"].clone(){
|
|
|
|
|
|
title = t;
|
|
|
|
|
|
}
|
|
|
|
|
|
if let serde_json::Value::String(t) = v["result"][0]["attributes"]["Supplier Footprint"].clone(){
|
|
|
|
|
|
footprint = t;
|
|
|
|
|
|
}
|
|
|
|
|
|
if let serde_json::Value::String(t) = v["result"][0]["attributes"]["Datasheet"].clone(){
|
|
|
|
|
|
datasheet = t;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut step_id = String::new();
|
|
|
|
|
|
let resp = search_model_id(has_device.as_str()).await?;
|
|
|
|
|
|
let mut model = String::new();
|
|
|
|
|
|
let v: serde_json::Value = serde_json::from_str(resp.as_str())?;
|
|
|
|
|
|
info!("In search_model_id: The v is : {v:#?}");
|
|
|
|
|
|
let data_str: serde_json::Value = v["result"][0]["dataStr"].clone();
|
|
|
|
|
|
|
|
|
|
|
|
if let serde_json::Value::String(data_str) = data_str {
|
|
|
|
|
|
let data_str: DataStr = serde_json::from_str(data_str.as_str())?;
|
|
|
|
|
|
if let Some(m) = data_str.model {
|
|
|
|
|
|
step_id = m;
|
|
|
|
|
|
}else{
|
|
|
|
|
|
return Err(anyhow::Error::msg("Failed to get model"));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut imgs = Vec::new();
|
|
|
|
|
|
for img_url in item.img_urls {
|
|
|
|
|
|
info!("Fetching img url: {}", img_url);
|
|
|
|
|
|
let img = reqwest::get(img_url).await?.bytes().await?;
|
|
|
|
|
|
// let img = image::load_from_memory(&img)?;
|
|
|
|
|
|
let img = iced::widget::image::Handle::from_bytes(img.clone());
|
|
|
|
|
|
imgs.push(img);
|
|
|
|
|
|
}
|
|
|
|
|
|
let rst = FetchResultItem {
|
|
|
|
|
|
name: item.name.clone(),
|
|
|
|
|
|
model_id: step_id,
|
|
|
|
|
|
model:footprint,
|
|
|
|
|
|
code:item.code,
|
|
|
|
|
|
imgs,
|
|
|
|
|
|
datasheet,
|
|
|
|
|
|
};
|
|
|
|
|
|
Ok(rst)
|
2025-06-28 16:43:09 +08:00
|
|
|
|
}
|
2025-07-02 21:26:19 +08:00
|
|
|
|
async fn search_has_device(has_device: &str) -> Result<String> {
|
|
|
|
|
|
let mut form_maps = std::collections::HashMap::new();
|
|
|
|
|
|
form_maps.insert("uuids[]", has_device);
|
|
|
|
|
|
let url = "https://pro.lceda.cn/api/devices/searchByIds";
|
|
|
|
|
|
let resp = reqwest::Client::new()
|
|
|
|
|
|
.post(url)
|
|
|
|
|
|
.form(&form_maps)
|
|
|
|
|
|
.send()
|
|
|
|
|
|
.await?;
|
|
|
|
|
|
let text = resp.text().await?;
|
|
|
|
|
|
Ok(text)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fn search_model_id(uuid: &str) -> Result<String> {
|
|
|
|
|
|
let mut form_maps = std::collections::HashMap::new();
|
|
|
|
|
|
form_maps.insert("uuids[]", uuid);
|
|
|
|
|
|
form_maps.insert("dataStr", "yes");
|
|
|
|
|
|
let url = "https://pro.lceda.cn/api/components/searchByIds?forceOnline=1";
|
2025-06-28 16:43:09 +08:00
|
|
|
|
|
2025-07-02 21:26:19 +08:00
|
|
|
|
let resp = reqwest::Client::new()
|
|
|
|
|
|
.post(url)
|
|
|
|
|
|
.form(&form_maps)
|
|
|
|
|
|
.send()
|
|
|
|
|
|
.await?;
|
|
|
|
|
|
let text = resp.text().await?;
|
|
|
|
|
|
info!("In search_model_id: The v is : {}",text.clone());
|
|
|
|
|
|
return Ok(text)
|
|
|
|
|
|
}
|
|
|
|
|
|
#[derive(Clone, Default, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
|
|
|
|
|
struct KeywordSearchRoot {
|
|
|
|
|
|
code: u32,
|
|
|
|
|
|
success: bool,
|
|
|
|
|
|
result: KeywordSearchResult,
|
|
|
|
|
|
}
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
|
#[derive(Default, Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq)]
|
|
|
|
|
|
struct KeywordSearchResult {
|
|
|
|
|
|
total: u32,
|
|
|
|
|
|
productList: Vec<KeywordSearchListItem>,
|
|
|
|
|
|
}
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq)]
|
|
|
|
|
|
struct KeywordSearchListItem {
|
|
|
|
|
|
id: u32,
|
|
|
|
|
|
code: String,
|
|
|
|
|
|
image: Option<KeywordChipImages>,
|
|
|
|
|
|
model: String,
|
|
|
|
|
|
hasDevice: Option<String>,
|
|
|
|
|
|
}
|
|
|
|
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq)]
|
|
|
|
|
|
struct KeywordChipImages(String);
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
|
impl KeywordChipImages {
|
|
|
|
|
|
pub fn imgs(&self) -> Vec<String> {
|
|
|
|
|
|
let urls = self.0.clone();
|
|
|
|
|
|
let list = urls.split("<$>");
|
|
|
|
|
|
let mut rst = Vec::new();
|
|
|
|
|
|
for i in list {
|
|
|
|
|
|
rst.push(i.to_string());
|
|
|
|
|
|
}
|
|
|
|
|
|
rst
|
2025-06-28 16:43:09 +08:00
|
|
|
|
}
|
2025-07-02 21:26:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
|
|
|
|
struct KeywordDevInfo {
|
|
|
|
|
|
uuid: String,
|
|
|
|
|
|
description: String,
|
|
|
|
|
|
title: String,
|
|
|
|
|
|
images: Vec<String>,
|
|
|
|
|
|
attributes: KeywordAttributes,
|
|
|
|
|
|
}
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
|
|
|
|
struct KeywordAttributes {
|
|
|
|
|
|
Datasheet: Option<String>,
|
|
|
|
|
|
}
|
|
|
|
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
|
|
|
|
struct DataStr {
|
|
|
|
|
|
model: Option<String>,
|
|
|
|
|
|
src: Option<String>,
|
|
|
|
|
|
_type: Option<String>,
|
|
|
|
|
|
}
|