diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c8b9c24 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5" +} \ No newline at end of file diff --git a/README.md b/README.md index 485d7a5..c3fe449 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,8 @@ >首先安装 TF版的 [scriptable](https://testflight.apple.com/join/uN1vTqxk) -> 网页安装:(推荐:⭐️⭐️⭐️⭐️⭐️)[WebStore](https://scriptablejs.gitee.io/store/#/) - -> 单个安装:(推荐:⭐️⭐️⭐️⭐️⭐️)[widget.Install](https://raw.githubusercontent.com/dompling/Scriptable/master/widget.Install.js) 安装导入到 scriptable,添加订阅,然后选择即可获取安装列表(覆盖当前下载文件) - -> 全量安装:(推荐:⭐️⭐️⭐️️)[2YaInstall](https://raw.githubusercontent.com/dompling/Scriptable/master/2YaInstall.js) 导入到 scriptable 软件中,点击运行即可(覆盖被修改的同名文件) - -> 收集订阅:(使用 [widget.Install](https://raw.githubusercontent.com/dompling/Scriptable/master/widget.Install.js)添加下方链接即可获取作者小组件) +> 网页安装:(推荐:⭐️⭐️⭐️⭐️⭐️)[WebStore](https://dompling.github.io/store/#/menu) +> 安装教程:[网页安装步骤](https://t.me/Scriptable_JS/101536) - 作者:@2Ya - 订阅安装包 diff --git a/Scripts/12123.js b/Scripts/12123.js new file mode 100644 index 0000000..3714c6c --- /dev/null +++ b/Scripts/12123.js @@ -0,0 +1,261 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: car; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +const API_PARAMS = { + api4: 'biz.vio.detail.query', + infoURL: 'https://miniappcsfw.122.gov.cn:8443/openapi/invokeApi/business/biz', + api1: 'biz.vio.unhandledVioCount.query', + productId: 'p10000000000000000001', + alipay: 'alipays://platformapi/startapp?appId=2019050964403523', + api2: 'biz.vio.peccancyChannelList.query', + status: + 'alipays://platformapi/startapp?appId=2019050964403523&page=pages%2Flicense%2Flicense', + update: 'https://gitcode.net/4qiao/scriptable/raw/master/api/violation.js', + api3: 'biz.vio.peccancyUnhandleInfoList.query', + Ver: 'Version 1.2\n\nverifyToken过期需打开Quantumult-X', +}; + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg, { + lightBgColor: '#2581f2', + darkBgColor: '#2581f2', + darkColor: '#fff', + lightColor: '#fff', + }); + this.en = '12123'; + this.name = '交管 12123'; + config.runsInApp && + this.registerAction({ + icon: { name: 'paperplane', color: '#722ed1' }, + type: 'input', + title: 'Token', + desc: '微信小程序交管12123获取', + val: 'Token', + onClick: async () => { + const token = this.settings.token; + this.settings.token = + (await this.getCache('wx_12123', false)) || token; + if (this.settings.token) this.saveSettings(false); + return this.setAlertInput('Token', '设置 token', { + token: '微信小程序交管12123获取', + }); + }, + }); + + config.runsInApp && this.registerAction('基础设置', this.setWidgetConfig); + } + + format = (str) => { + return parseInt(str) >= 10 ? str : `0${str}`; + }; + + date = new Date(); + arrUpdateTime = [ + this.format(this.date.getMonth() + 1), + this.format(this.date.getDate()), + this.format(this.date.getHours()), + this.format(this.date.getMinutes()), + ]; + + dataSource = { + left: { + title: '川 G88888', + icon: 'car.fill', + listItem: [ + { label: '未处违法', value: `0`, unit: '条' }, + { label: '车辆状态', value: '正常' }, + { label: '上次更新', value: '00:00' }, + ], + }, + right: { + title: '驾驶证', + icon: 'creditcard.fill', + listItem: [ + { label: '证件状态', value: '正常' }, + { label: '累计扣分', value: `0`, unit: '分' }, + { label: '重置日期', value: '—' }, + ], + }, + }; + + init = async () => { + this.settings.token = + (await this.getCache('wx_12123', false)) || this.settings.token; + if (this.settings.dataSource) { + this.dataSource = this.settings.dataSource; + } else { + await this.cacheData(); + } + this.cacheData(); + }; + + cacheData = async () => { + try { + const token = this.settings.token.replace('params=', ''); + const body = JSON.parse(decodeURIComponent(token)); + const params = { + sign: body.sign, + // businessId: body.businessId, + verifyToken: body.verifyToken, + // businessPrincipalId: body.businessPrincipalId, + }; + + const response = await this.$request.post(API_PARAMS.infoURL, { + body: `params=${JSON.stringify({ + api: API_PARAMS.api1, + productId: API_PARAMS.productId, + ...params, + })}`, + }); + console.log(response); + if (response.success) { + const illegal = response.data.list[0] || {}; + this.dataSource.left.listItem[0].value = illegal.count || 0; + + const details = await this.$request.post(API_PARAMS.infoURL, { + body: `params=${encodeURIComponent( + JSON.stringify({ + api: 'biz.user.integration.query', + productId: API_PARAMS.productId, + ...params, + }) + )}`, + }); + + console.log(details); + + if (details.success) { + const { drivingLicense, vehicles } = details.data; + const reaccDate = drivingLicense.reaccDate.split('-'); + this.dataSource.right.title = `驾驶证 ${drivingLicense.allowToDrive}`; + this.dataSource.right.listItem[1].value = + drivingLicense.cumulativePoint; + this.dataSource.right.listItem[2].value = `${reaccDate[1]}-${reaccDate[2]}`; + + if (vehicles.length) { + this.dataSource.left.title = vehicles[0].plateNumber; + } + } + + this.dataSource.left.listItem[2].value = `${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`; + + this.settings.dataSource = this.dataSource; + this.saveSettings(false); + } else { + this.notify( + `verifyToken已过期 ⚠️`, + '点击通知框自动跳转到支付宝小程序交管12123页面获取最新的Token ( 请确保已打开辅助工具 )', + API_PARAMS.alipay + ); + } + } catch (e) { + console.log(e); + } + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + renderSmall = async (w) => { + this.notSupport(w); + return w; + }; + + renderLarge = async (w) => { + return this.notSupport(w); + }; + + renderCard = (w, data) => { + w.borderColor = this.widgetColor; + w.borderWidth = 2; + w.cornerRadius = 8; + + w.layoutVertically(); + w.setPadding(10, 10, 10, 10); + const topStack = w.addStack(); + topStack.layoutHorizontally(); + topStack.centerAlignContent(); + const iconImage = SFSymbol.named(data.icon).image; + const iconImageStack = topStack.addImage(iconImage); + iconImageStack.tintColor = this.widgetColor; + iconImageStack.imageSize = new Size(30, 30); + + topStack.addSpacer(10); + + const licensePlateText = topStack.addText(data.title); + licensePlateText.textColor = this.widgetColor; + licensePlateText.font = this.provideFont('bold', 14); + + w.addSpacer(); + + data.listItem.forEach((item, index) => { + const listItemStack = w.addStack(); + listItemStack.centerAlignContent(); + const labelText = listItemStack.addText(item.label); + labelText.textColor = this.widgetColor; + labelText.font = this.provideFont('medium', 14); + + listItemStack.addSpacer(); + if (index !== data.listItem.length - 1) w.addSpacer(); + + const valueText = listItemStack.addText(`${item.value}`); + valueText.textColor = this.widgetColor; + valueText.font = this.provideFont('medium', 14); + + if (item.unit) { + const unitText = listItemStack.addText(item.unit); + unitText.textColor = this.widgetColor; + unitText.font = this.provideFont('medium', 14); + } + }); + }; + + renderMedium = async (w) => { + const containerStack = w.addStack(); + containerStack.layoutHorizontally(); + containerStack.centerAlignContent(); + + const leftStack = containerStack.addStack(); + this.renderCard(leftStack, this.dataSource.left); + + containerStack.addSpacer(10); + + const rightStack = containerStack.addStack(); + this.renderCard(rightStack, this.dataSource.right); + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/BiliBiliWatch.js b/Scripts/BiliBiliWatch.js index 9a5e574..6e8d751 100644 --- a/Scripts/BiliBiliWatch.js +++ b/Scripts/BiliBiliWatch.js @@ -3,21 +3,21 @@ // icon-color: gray; icon-glyph: chalkboard; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = "哔哩哔哩关注"; - this.en = "BiliBiliWatch"; + this.name = '哔哩哔哩关注'; + this.en = 'BiliBiliWatch'; this.logo = - "https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png"; + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png'; this.Run(module.filename); } - cookie = ""; + cookie = ''; dataSource = []; init = async () => { @@ -33,7 +33,7 @@ class Widget extends DmYY { const method = `GET`; const headers = { Cookie: this.cookie, - "User-Agent": `bili-universal/10320 CFNetwork/1206 Darwin/20.1.0 os/ios model/iPhone XR mobi_app/iphone build/10320 osVer/14.2 network/2 channel/AppStore`, + 'User-Agent': `bili-universal/10320 CFNetwork/1206 Darwin/20.1.0 os/ios model/iPhone XR mobi_app/iphone build/10320 osVer/14.2 network/2 channel/AppStore`, }; const response = await this.$request.get(url, { method, @@ -69,10 +69,10 @@ class Widget extends DmYY { }); return this.dataSource; } else { - throw "cookie 失效,请重新获取"; + throw 'cookie 失效,请重新获取'; } } catch (e) { - console.log("❌错误信息:" + e); + console.log('❌错误信息:' + e); return false; } }; @@ -81,11 +81,11 @@ class Widget extends DmYY { const { title, url, reply, play, desc, img, timestamp } = data; let body = cell.addStack(); body.url = url; - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { const imageView = body.addStack(); imageView.size = new Size(43, 43); imageView.cornerRadius = 5; - const image = await this.$request.get(img, "IMG"); + const image = await this.$request.get(img, 'IMG'); imageView.backgroundImage = image; body.addSpacer(10); } @@ -112,7 +112,7 @@ class Widget extends DmYY { const descView = textView.addStack(); - const icon1 = descView.addText("浏览:"); + const icon1 = descView.addText('浏览:'); icon1.font = Font.lightSystemFont(10); icon1.textColor = this.widgetColor; descView.addSpacer(3); @@ -121,7 +121,7 @@ class Widget extends DmYY { timerText.textColor = this.widgetColor; descView.addSpacer(5); - const icon2 = descView.addText("评论:"); + const icon2 = descView.addText('评论:'); icon2.font = Font.lightSystemFont(10); icon2.textColor = this.widgetColor; @@ -167,15 +167,15 @@ class Widget extends DmYY { const widget = new ListWidget(); await this.getWidgetBackgroundImage(widget); const header = widget.addStack(); - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { await this.renderJDHeader(header); } else { await this.renderHeader(header, this.logo, this.name, this.widgetColor); } widget.addSpacer(10); - if (this.widgetFamily === "medium") { + if (this.widgetFamily === 'medium') { return await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { + } else if (this.widgetFamily === 'large') { return await this.renderLarge(widget); } else { return await this.renderSmall(widget); @@ -187,11 +187,11 @@ class Widget extends DmYY { await this.renderHeader(header, this.logo, this.name, this.widgetColor); header.addSpacer(); const headerMore = header.addStack(); - headerMore.url = ""; + headerMore.url = ''; headerMore.setPadding(1, 10, 1, 10); headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText("个人中心"); + headerMore.backgroundColor = new Color('#fff', 0.5); + const textItem = headerMore.addText('个人中心'); textItem.font = Font.boldSystemFont(12); textItem.textColor = this.widgetColor; textItem.lineLimit = 1; @@ -201,55 +201,49 @@ class Widget extends DmYY { Run = (filename) => { if (config.runsInApp) { - this.registerAction("基础设置", this.setWidgetConfig); - this.registerAction("账号设置", this.inputCk); - this.registerAction("代理缓存", this._loadCk); + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('账号设置', this.inputCk); + this.registerAction('代理缓存', this._loadCk); } let _md5 = this.md5(filename + this.en); this.CACHE_KEY = `cache_${_md5}`; try { this.cookie = this.settings[this.en]; if (!this.cookie) { - throw "CK 获取失败"; + throw 'CK 获取失败'; } return true; } catch (e) { - this.notify("错误提示", e); + this.notify('错误提示', e); return false; } }; _loadCk = async () => { try { - const cookie = await this.getCache("@bilibili.cookie"); + const cookie = await this.getCache('@bilibili.cookie'); if (cookie) { this.cookie = cookie; this.settings[this.en] = this.cookie; this.saveSettings(); } else { - throw "ck 获取失败"; + throw 'ck 获取失败'; } return true; } catch (e) { console.log(e); - this.cookie = ""; - this.notify( - this.name, - "", - "BoxJS 数据读取失败,请点击通知查看教程", - "https://chavyleung.gitbook.io/boxjs/awesome/videos" - ); + this.cookie = ''; return false; } }; async inputCk() { const a = new Alert(); - a.title = "账号设置"; - a.message = "手动输入 Ck"; - a.addTextField("Cookie", this.cookie); - a.addAction("确定"); - a.addCancelAction("取消"); + a.title = '账号设置'; + a.message = '手动输入 Ck'; + a.addTextField('Cookie', this.cookie); + a.addAction('确定'); + a.addCancelAction('取消'); const id = await a.presentAlert(); if (id === -1) return; this.cookie = a.textFieldValue(0); @@ -261,4 +255,4 @@ class Widget extends DmYY { // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/Birthday.js b/Scripts/Birthday.js deleted file mode 100644 index ef78a85..0000000 --- a/Scripts/Birthday.js +++ /dev/null @@ -1,307 +0,0 @@ -// Variables used by Scriptable. -// These must be at the very top of the file. Do not edit. -// icon-color: pink; icon-glyph: birthday-cake; - -// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); -const { Calendar } = require("./Calendar"); -const $ = new Calendar(); - -class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "破壳日"; - this.en = "birthday"; - this.logo = - "https://raw.githubusercontent.com/Orz-3/mini/master/Color/birthday.png"; - this.LEFT_IMG_KEY = this.FILE_MGR_LOCAL.joinPath( - this.FILE_MGR_LOCAL.documentsDirectory(), - `left_image_${this.SETTING_KEY}.jpg`, - ); - this.defaultData = { ...this.defaultData, ...this.settings[this.en] }; - if (config.runsInApp) { - this.registerAction("基础设置", this.setWidgetConfig); - this.registerAction("生日配置", this.setWidgetInitConfig); - this.registerAction("头像设置", this.setLeftWidgetImage); - this.registerAction("代理缓存", this.setWidgetBoxJSConfig); - } - } - - defaultData = { - username: "", // 姓名 - time: "", // 生日日期 - nongli: "", // 农历生日 - eday: "", //相识 - isLeapMonth: false, //如果是农历闰月第四个参数赋值true即可 - }; - - contentText = {}; - - init = async () => { - try { - this.getCalendarData(); - } catch (e) { - console.log(e); - } - }; - - getEdayNumber = (date) => { - var initDay = date.split("-"); - var obj = { - cYear: parseInt(initDay[0]), - cMonth: parseInt(initDay[1]), - cDay: parseInt(initDay[2]), - }; - return Math.abs(this.$.daysBetween(obj)); - }; - - getCalendarData = () => { - const { time, nongli, isLeapMonth, eday } = this.defaultData; - const _data = time.split("-"); - const opt = { - year: parseInt(_data[0]), - month: parseInt(_data[1]), - day: parseInt(_data[2]), - nongli, - isLeapMonth, - }; - - const response = {}; - response.birthdayText = this.$.birthday(opt); - response.nextBirthday = response.birthdayText[0]; - - const solarData = - nongli === "true" - ? this.$.lunar2solar(opt.year, opt.month, opt.day, isLeapMonth) - : this.$.solar2lunar(opt.year, opt.month, opt.day); - response.gregorian = solarData; - response.animal = `${this.$.getAnimalZodiacToEmoji(solarData.Animal)}-${ - solarData.Animal - }`; - response.astro = `${this.$.getAstroToEmoji(solarData.astro)}-${solarData.astro}`; - if (this.$.verifyTime(eday)) { - response.meetDay = this.getEdayNumber(eday); - } - this.contentText = response; - }; - - setRightCell = (text, rowCell) => { - const subContent = rowCell.addText(text); - subContent.font = Font.boldSystemFont(14); - subContent.textColor = this.widgetColor; - subContent.lineLimit = 1; - rowCell.addSpacer(5); - }; - - setLeftView = (w) => { - const leftImg = this.getLeftImage(); - const left = w.addStack(); - left.size = new Size(110, 110); - left.cornerRadius = 5; - left.borderWidth = 2; - left.borderColor = this.widgetColor; - if (leftImg) { - const widgetImg = left.addImage(leftImg); - widgetImg.imageSize = new Size(110, 110); - widgetImg.applyFillingContentMode(); - widgetImg.cornerRadius = 5; - } - return w; - }; - - setRightView = (right) => { - const { - animal, - astro, - gregorian, - nextBirthday, - meetDay, - birthdayText, - } = this.contentText; - const { IMonthCn, IDayCn } = gregorian; - right.layoutVertically(); - this.setRightCell(`🐽相:${animal}`, right); // 属相 - this.setRightCell(`🌠座:${astro}`, right); // 属相 - if (meetDay) { - this.setRightCell(`💖遇:${meetDay} 天`, right); - } - const _birth = `🎂生:${nextBirthday.cYear}-${nextBirthday.cMonth}-${nextBirthday.cDay} (${birthdayText[1]}天)`; - this.setRightCell(_birth, right); - this.setRightCell(`📆农:${IMonthCn}${IDayCn}`, right); - return right; - }; - - fetch = async () => { - const response = await this.$request.get( - "https://api.uomg.com/api/rand.qinghua?format=json", - ); - return response.content; - }; - - renderSmall = async (w) => { - this.setRightView(w.addStack()); - return w; - }; - - renderLarge = async (w) => { - w.addSpacer(20); - const body = w.addStack(); - const left = body.addStack(); - this.setLeftView(left); - body.addSpacer(20); - const right = body.addStack(); - this.setRightView(right); - - w.addSpacer(20); - const footer = w.addStack(); - const text = await this.fetch(); - const subContent = footer.addText(text); - subContent.font = Font.boldSystemFont(16); - subContent.textColor = this.widgetColor; - w.addSpacer(); - return w; - }; - - renderMedium = async (w) => { - const body = w.addStack(); - const left = body.addStack(); - this.setLeftView(left); - body.addSpacer(); - const right = body.addStack(); - this.setRightView(right); - body.addSpacer(); - w.addSpacer(); - return w; - }; - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - const header = widget.addStack(); - if (this.widgetFamily !== "small") { - await this.renderMoreHeader(header); - } else { - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - } - widget.addSpacer(10); - if (this.widgetFamily === "medium") { - await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - await this.renderLarge(widget); - } else { - await this.renderSmall(widget); - } - return widget; - } - - renderMoreHeader = async (header) => { - header.centerAlignContent(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - header.addSpacer(); - const headerMore = header.addStack(); - headerMore.setPadding(1, 10, 1, 10); - headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText(this.defaultData.username); - textItem.font = Font.boldSystemFont(12); - textItem.textColor = this.widgetColor; - textItem.lineLimit = 1; - textItem.rightAlignText(); - return header; - }; - - /** - * 获取当前插件是否有自定义背景图片 - * @reutrn img | false - */ - getLeftImage() { - let result = null; - if (this.FILE_MGR_LOCAL.fileExists(this.LEFT_IMG_KEY)) { - result = Image.fromFile(this.LEFT_IMG_KEY); - } - return result; - } - - /** - * 设置当前组件的背景图片 - * @param {image} img - */ - setLeftImage(img, notify = true) { - if (!img) { - // 移除背景 - if (this.FILE_MGR_LOCAL.fileExists(this.LEFT_IMG_KEY)) { - this.FILE_MGR_LOCAL.remove(this.LEFT_IMG_KEY); - } - if (notify) this.notify("移除成功", "小组件图片已移除,稍后刷新生效"); - } else { - // 设置背景 - // 全部设置一遍, - this.FILE_MGR_LOCAL.writeImage(this.LEFT_IMG_KEY, img); - if (notify) this.notify("设置成功", "小组件图片已设置!稍后刷新生效"); - } - } - - setLeftWidgetImage = async () => { - const alert = new Alert(); - alert.title = "设置左侧图"; - alert.message = "显示左侧图片"; - alert.addAction("设置新图"); - alert.addAction("清空图片"); - alert.addCancelAction("取消"); - const actions = [ - async () => { - const backImage = await this.chooseImg(); - if (!await this.verifyImage(backImage)) return; - await this.setLeftImage(backImage, true); - }, - () => { - this.setLeftImage(false, true); - }, - ]; - const id = await alert.presentAlert(); - if (id === -1) return; - actions[id] && actions[id].call(this); - }; - - setWidgetInitConfig = async () => { - const a = new Alert(); - a.title = "🐣破壳日配置"; - a.message = "配置破壳日的基础信息"; - a.addTextField("昵称", this.defaultData.username); - a.addTextField("生日/ 年-月-日", this.defaultData.time); - a.addTextField("农历/ true | false", `${this.defaultData.nongli || ""}`); - a.addTextField("相识/ 年-月-日", this.defaultData.eday); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - this.defaultData.username = a.textFieldValue(0); - this.defaultData.time = a.textFieldValue(1); - this.defaultData.nongli = a.textFieldValue(2) === "true"; - this.defaultData.eday = a.textFieldValue(3); - // 保存到本地 - this.settings[this.en] = this.defaultData; - this.saveSettings(); - }; - - setWidgetBoxJSConfig = async () => { - try { - const datas = await this.getCache(); - Object.keys(this.defaultData).forEach((key) => { - this.defaultData[key] = datas[`@${this.en}.${key}`]; - }); - this.settings[this.en] = this.defaultData; - this.saveSettings(); - } catch (e) { - this.notify(this.name, "", "BoxJS 数据读取失败,请点击通知查看教程", "https://chavyleung.gitbook.io/boxjs/awesome/videos"); - } - }; -} - -Runing(Widget, "", false, { $ }); diff --git a/Scripts/CalendarFnc.js b/Scripts/CalendarFnc.js new file mode 100644 index 0000000..a8b5f21 --- /dev/null +++ b/Scripts/CalendarFnc.js @@ -0,0 +1 @@ +function calendarFun(){const calendar={lunarInfo:[0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50,0x06b20,0x1a6c4,0x0aae0,0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,0x0d520,],solarMonth:[31,28,31,30,31,30,31,31,30,31,30,31],Gan:["\u7532","\u4e59","\u4e19","\u4e01","\u620a","\u5df1","\u5e9a","\u8f9b","\u58ec","\u7678",],Zhi:["\u5b50","\u4e11","\u5bc5","\u536f","\u8fb0","\u5df3","\u5348","\u672a","\u7533","\u9149","\u620c","\u4ea5",],Animals:["\u9f20","\u725b","\u864e","\u5154","\u9f99","\u86c7","\u9a6c","\u7f8a","\u7334","\u9e21","\u72d7","\u732a",],solarTerm:["\u5c0f\u5bd2","\u5927\u5bd2","\u7acb\u6625","\u96e8\u6c34","\u60ca\u86f0","\u6625\u5206","\u6e05\u660e","\u8c37\u96e8","\u7acb\u590f","\u5c0f\u6ee1","\u8292\u79cd","\u590f\u81f3","\u5c0f\u6691","\u5927\u6691","\u7acb\u79cb","\u5904\u6691","\u767d\u9732","\u79cb\u5206","\u5bd2\u9732","\u971c\u964d","\u7acb\u51ac","\u5c0f\u96ea","\u5927\u96ea","\u51ac\u81f3",],sTermInfo:["9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","9778397bd19801ec9210c965cc920e","97b6b97bd19801ec95f8c965cc920f","97bd09801d98082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd197c36c9210c9274c91aa","97b6b97bd19801ec95f8c965cc920e","97bd09801d98082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec95f8c965cc920e","97bcf97c3598082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd07f595b0b6fc920fb0722","9778397bd097c36b0b6fc9210c8dc2","9778397bd19801ec9210c9274c920e","97b6b97bd19801ec95f8c965cc920f","97bd07f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c920e","97b6b97bd19801ec95f8c965cc920f","97bd07f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec9210c965cc920e","97bd07f1487f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c9274c920e","97bcf7f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c91aa","97b6b97bd197c36c9210c9274c920e","97bcf7f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c920e","97b6b7f0e47f531b0723b0b6fb0722","7f0e37f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36b0b70c9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e37f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc9210c8dc2","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0787b0721","7f0e27f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c91aa","97b6b7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c8dc2","977837f0e37f149b0723b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f5307f595b0b0bc920fb0722","7f0e397bd097c35b0b6fc9210c8dc2","977837f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0721","7f0e37f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc9210c8dc2","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0723b06bd","7f07e7f0e37f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f1487f595b0b0bb0b6fb0722","7f0e37f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e37f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0723b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0723b06bd","7f07e7f0e37f14998083b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14898082b0723b02d5","7f07e7f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e36665b66aa89801e9808297c35","665f67f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e36665b66a449801e9808297c35","665f67f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e36665b66a449801e9808297c35","665f67f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e26665b66a449801e9808297c35","665f67f0e37f1489801eb072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722",],nStr1:["\u65e5","\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341",],nStr2:["\u521d","\u5341","\u5eff","\u5345"],nStr3:["\u6b63","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u51ac","\u814a",],lYearDays:function(y){var i,sum=348;for(i=0x8000;i>0x8;i>>=1){sum+=calendar.lunarInfo[y-1900]&i?1:0}return sum+calendar.leapDays(y)},leapMonth:function(y){return calendar.lunarInfo[y-1900]&0xf},leapDays:function(y){if(calendar.leapMonth(y)){return calendar.lunarInfo[y-1900]&0x10000?30:29}return 0},monthDays:function(y,m){if(m>12||m<1){return-1}return calendar.lunarInfo[y-1900]&(0x10000>>m)?30:29},solarDays:function(y,m){if(m>12||m<1){return-1}var ms=m-1;if(ms==1){return(y%4==0&&y%100!=0)||y%400==0?29:28}else{return calendar.solarMonth[ms]}},toGanZhiYear:function(lYear){var ganKey=(lYear-3)%10;var zhiKey=(lYear-3)%12;if(ganKey==0)ganKey=10;if(zhiKey==0)zhiKey=12;return calendar.Gan[ganKey-1]+calendar.Zhi[zhiKey-1]},toAstro:function(cMonth,cDay){var s="\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf";var arr=[20,19,21,21,21,22,23,23,23,23,22,22];return(s.substr(cMonth*2-(cDay2100){return-1}if(n<1||n>24){return-1}var _table=calendar.sTermInfo[y-1900];var _info=[parseInt("0x"+_table.substr(0,5)).toString(),parseInt("0x"+_table.substr(5,5)).toString(),parseInt("0x"+_table.substr(10,5)).toString(),parseInt("0x"+_table.substr(15,5)).toString(),parseInt("0x"+_table.substr(20,5)).toString(),parseInt("0x"+_table.substr(25,5)).toString(),];var _calday=[_info[0].substr(0,1),_info[0].substr(1,2),_info[0].substr(3,1),_info[0].substr(4,2),_info[1].substr(0,1),_info[1].substr(1,2),_info[1].substr(3,1),_info[1].substr(4,2),_info[2].substr(0,1),_info[2].substr(1,2),_info[2].substr(3,1),_info[2].substr(4,2),_info[3].substr(0,1),_info[3].substr(1,2),_info[3].substr(3,1),_info[3].substr(4,2),_info[4].substr(0,1),_info[4].substr(1,2),_info[4].substr(3,1),_info[4].substr(4,2),_info[5].substr(0,1),_info[5].substr(1,2),_info[5].substr(3,1),_info[5].substr(4,2),];return parseInt(_calday[n-1])},toChinaMonth:function(m){if(m>12||m<1){return-1}var s=calendar.nStr3[m-1];s+="\u6708";return s},toChinaDay:function(d){var s;switch(d){case 10:s="\u521d\u5341";break;case 20:s="\u4e8c\u5341";break;break;case 30:s="\u4e09\u5341";break;break;default:s=calendar.nStr2[Math.floor(d/10)];s+=calendar.nStr1[d%10]}return s},getAnimal:function(y){return calendar.Animals[(y-4)%12]},solar2lunar:function(y,m,d){if(y<1900||y>2100){return-1}if(y==1900&&m==1&&d<31){return-1}if(!y){var objDate=new Date()}else{var objDate=new Date(y,parseInt(m)-1,d)}var i,leap=0,temp=0;var y=objDate.getFullYear(),m=objDate.getMonth()+1,d=objDate.getDate();var offset=(Date.UTC(objDate.getFullYear(),objDate.getMonth(),objDate.getDate())-Date.UTC(1900,0,31))/86400000;for(i=1900;i<2101&&offset>0;i++){temp=calendar.lYearDays(i);offset-=temp}if(offset<0){offset+=temp;i--}var isTodayObj=new Date(),isToday=false;if(isTodayObj.getFullYear()==y&&isTodayObj.getMonth()+1==m&&isTodayObj.getDate()==d){isToday=true}var nWeek=objDate.getDay(),cWeek=calendar.nStr1[nWeek];if(nWeek==0){nWeek=7}var year=i;var leap=calendar.leapMonth(i);var isLeap=false;for(i=1;i<13&&offset>0;i++){if(leap>0&&i==leap+1&&isLeap==false){--i;isLeap=true;temp=calendar.leapDays(year)}else{temp=calendar.monthDays(year,i)}if(isLeap==true&&i==leap+1){isLeap=false}offset-=temp}if(offset==0&&leap>0&&i==leap+1){if(isLeap){isLeap=false}else{isLeap=true;--i}}if(offset<0){offset+=temp;--i}var month=i;var day=offset+1;var sm=m-1;var gzY=calendar.toGanZhiYear(year);var firstNode=calendar.getTerm(y,m*2-1);var secondNode=calendar.getTerm(y,m*2);var gzM=calendar.toGanZhi((y-1900)*12+m+11);if(d>=firstNode){gzM=calendar.toGanZhi((y-1900)*12+m+12)}var isTerm=false;var Term=null;if(firstNode==d){isTerm=true;Term=calendar.solarTerm[m*2-2]}if(secondNode==d){isTerm=true;Term=calendar.solarTerm[m*2-1]}var dayCyclical=Date.UTC(y,sm,1,0,0,0,0)/86400000+25567+10;var gzD=calendar.toGanZhi(dayCyclical+d-1);var astro=calendar.toAstro(m,d);return{lYear:year,lMonth:month,lDay:day,Animal:calendar.getAnimal(year),IMonthCn:(isLeap?"\u95f0":"")+calendar.toChinaMonth(month),IDayCn:calendar.toChinaDay(day),cYear:y,cMonth:m,cDay:d,gzYear:gzY,gzMonth:gzM,gzDay:gzD,isToday:isToday,isLeap:isLeap,nWeek:nWeek,ncWeek:"\u661f\u671f"+cWeek,isTerm:isTerm,Term:Term,astro:astro,}},lunar2solar:function(y,m,d,isLeapMonth){var isLeapMonth=!!isLeapMonth;var leapOffset=0;var leapMonth=calendar.leapMonth(y);var leapDay=calendar.leapDays(y);if(isLeapMonth&&leapMonth!=m){return-1}if((y==2100&&m==12&&d>1)||(y==1900&&m==1&&d<31)){return-1}var day=calendar.monthDays(y,m);var _day=day;if(isLeapMonth){_day=calendar.leapDays(y,m)}if(y<1900||y>2100||d>_day){return-1}var offset=0;for(var i=1900;i0){offset+=calendar.leapDays(y);isAdd=true}}offset+=calendar.monthDays(y,i)}if(isLeapMonth){offset+=day}var stmap=Date.UTC(1900,1,30,0,0,0);var calObj=new Date((offset+d-31)*86400000+stmap);var cY=calObj.getUTCFullYear();var cM=calObj.getUTCMonth()+1;var cD=calObj.getUTCDate();return calendar.solar2lunar(cY,cM,cD)},birthday:function(y,m,d,nongli,isLeapMonth){var date;var now=new Date();if(nongli){var now_d=this.solar2lunar(now.getFullYear(),now.getMonth()+1,now.getDate());var now_year=now_d.lYear;date=this.birthBylunar(now_year,m,d,isLeapMonth);if(this.daysBetween(date)<=0){now_year++;date=this.birthBylunar(now_year,m,d,isLeapMonth)}}else{var now_year=now.getFullYear();date=this.solar2lunar(now_year,m,d);if(this.daysBetween(date)<=0){now_year++;date=this.solar2lunar(now_year,m,d)}}return[date.cYear,date.cMonth,date.cDay,this.daysBetween(date)]},birthBylunar:function(y,m,d,isLeapMonth){if(isLeapMonth&&this.leapMonth(y)==m){d=this.lunar2solar(y,m,d,isLeapMonth)}else{d=this.lunar2solar(y,m,d,false)}return d},daysBetween:function(d){var now=new Date();var date=new Date(d.cYear,d.cMonth-1,d.cDay);return parseInt((date.getTime()-now.getTime())/(24*3600*1000))},verifyTime(date){var dateFormat=/^(\d{4})-(\d{1,2})-(\d{1,2})$/;return dateFormat.test(date)},getAnimalZodiacToEmoji(zodiac){var data={鼠:"🐭",牛:"🐂",虎:"🐯",兔:"🐇",龙:"🐲",蛇:"🐍",马:"🐴",羊:"🐑",猴:"🐵",鸡:"🐔",狗:"🐶",猪:"🐷",};return data[zodiac]||""},getAstroToEmoji(astro){var data={白羊座:"♈",金牛座:"♉",双子座:"♊",巨蟹座:"♋",狮子座:"♌",处女座:"♍",天秤座:"♎",天蝎座:"♏",射手座:"♐",摩羯座:"♑",水瓶座:"♒",双鱼座:"♓",蛇夫座:"⛎",};return data[astro]||""},};return calendar} \ No newline at end of file diff --git a/Scripts/CarWidget.js b/Scripts/CarWidget.js new file mode 100644 index 0000000..02cb286 --- /dev/null +++ b/Scripts/CarWidget.js @@ -0,0 +1,342 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: car; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + if (config.runsInApp) { + this.registerAction({ + icon: { name: 'paperplane', color: '#722ed1' }, + type: 'input', + title: '油价设置', + desc: '89|92|95|0', + val: 'oilNumber', + }); + + this.registerAction({ + icon: { name: 'car', color: '#f5222d' }, + type: 'input', + title: '依赖插件', + desc: '汽车的依赖插件例如 Ftms', + val: 'filePath', + }); + + this.registerAction({ + icon: { name: 'plus.viewfinder', color: '#fa8c16' }, + type: 'input', + title: '缩放比例', + desc: '比例越大进度条越长', + placeholder: '取值 0~1', + val: 'scale', + }); + } + + config.runsInApp && this.registerAction('基础设置', this.setWidgetConfig); + this.cacheName = this.md5(`dataSouce_${this.en}`); + const filePath = this.settings.filePath || 'Ftms'; + const carModule = require(`./${filePath}`); + const carService = new carModule(this); + this.scale = parseFloat(this.settings.scale) || 1; // 柱状图比例高度,值越大,柱状范围越广 + this.init = carService.init; + this.name = carService.name; + this.logo = carService.logo; + this.viewColor = Color.dynamic( + new Color('#d9d9d9', parseFloat(this.settings.lightOpacity || 1)), + new Color('#8c8c8c', parseFloat(this.settings.darkOpacity || 1)) + ); + } + + widgetHeight = 145; + + serveInfo = { + carNumber: '', + }; + + dataSource = { + remoteInfo: { + datatime: '', + list: [], + userId: '', + carNumber: '', + }, + monitorInfo: { + km: '0', + oilRate: '0', + oilWaste: '0', + oilWasteText: '', + }, + safeText: '', + oilPriceText: '', + oilZDE: 0, + }; + + createProgressBar( + soFar, + total = 100, + width = 400, + height = 40, + showPercentage = false + ) { + const context = new DrawContext(); + context.size = new Size(width, height); + context.opaque = false; + context.respectScreenScale = true; + + // bar background + context.setFillColor(new Color('#48484b')); + const bgPath = new Path(); + bgPath.addRoundedRect( + new Rect(0, 0, width, height), + height / 2, + height / 2 - 1 + ); + context.addPath(bgPath); + context.fillPath(); + + // bar foreground + context.setFillColor(new Color('#e8e8e8')); + const fgPath = new Path(); + fgPath.addRoundedRect( + new Rect(0, 0, (width * soFar) / total, height), + height / 2, + height / 2 - 1 + ); + context.addPath(fgPath); + context.fillPath(); + + if (showPercentage) { + const percentage = ((soFar / total) * 100).toFixed(2); + let xPos = (width * soFar) / total + 5; + // if over 70%, show in foreground area + // to ensure that it doesn't overflow the display + if (percentage > 70) { + xPos = (width * soFar) / total - 55; + } + + context.setFont(Font.semiboldRoundedSystemFont(14)); + context.setTextColor(primaryTextColor); + context.drawText(`${percentage}%`, new Point(xPos, height / 14)); + } + + return context.getImage(); + } + + renderBorder = (stack) => { + stack.borderWidth = 1; + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + renderSmall = async (w) => { + w.addSpacer(); + + const stack = w.addStack(); + stack.layoutVertically(); + const headerStack = stack.addStack(); + headerStack.centerAlignContent(); + headerStack.addSpacer(10); + const gasImg = SFSymbol.named('fuelpump').image; + + const gasIcon = headerStack.addImage(gasImg); + gasIcon.imageSize = new Size(16, 16); + gasIcon.tintColor = this.widgetColor; + headerStack.addSpacer(5); + + const oilRateStackText = headerStack.addText( + `${this.dataSource.monitorInfo.oilRate}%` + ); + oilRateStackText.textColor = this.widgetColor; + oilRateStackText.font = Font.boldSystemFont(14); + + headerStack.addSpacer(); + const logImg = await this.renderImage(this.logo); + const logImgStack = headerStack.addImage(logImg); + logImgStack.imageSize = new Size(20, 20); + headerStack.addSpacer(10); + + const bodyStack = stack.addStack(); + bodyStack.centerAlignContent(); + bodyStack.addSpacer(); + const progressImg = this.createProgressBar( + this.dataSource.monitorInfo.oilRate + ); + const progressBar = bodyStack.addImage(progressImg); + progressBar.imageSize = new Size(this.widgetHeight * this.scale, 28); + bodyStack.addSpacer(); + + stack.addSpacer(); + + const oilWasteStack = stack.addStack(); + oilWasteStack.centerAlignContent(); + oilWasteStack.addSpacer(); + const oilWasteStackText = oilWasteStack.addText( + this.dataSource.monitorInfo.oilWasteText + ); + oilWasteStackText.textColor = this.widgetColor; + oilWasteStackText.font = Font.boldSystemFont(10); + oilWasteStack.addSpacer(5); + const oilPriceStackText = oilWasteStack.addText( + this.dataSource.oilPriceText + ); + oilPriceStackText.textColor = this.widgetColor; + oilPriceStackText.font = Font.boldSystemFont(10); + oilWasteStack.addSpacer(2); + const oilStatus = this.dataSource.oilZDE > 0; + const oilZdeImage = SFSymbol.named( + oilStatus ? 'arrow.up' : 'arrow.down' + ).image; + + const oilZdeWidgetImg = oilWasteStack.addImage(oilZdeImage); + oilZdeWidgetImg.tintColor = new Color(oilStatus ? '#f5222d' : '#a0d911'); + oilZdeWidgetImg.imageSize = new Size(10, 10); + + oilWasteStack.addSpacer(); + + const kilometerStack = stack.addStack(); + + kilometerStack.centerAlignContent(); + kilometerStack.addSpacer(); + const panoImg = SFSymbol.named('speedometer').image; + + const panoImgStack = kilometerStack.addStack(); + panoImgStack.setPadding(5, 0, 0, 0); + const panoStack = panoImgStack.addImage(panoImg); + panoStack.tintColor = this.widgetColor; + panoStack.imageSize = new Size(20, 20); + kilometerStack.addSpacer(5); + + const oilWasteText = kilometerStack.addText(this.dataSource.monitorInfo.km); + oilWasteText.font = Font.boldSystemFont(28); + oilWasteText.textColor = this.widgetColor; + kilometerStack.addSpacer(5); + const unitStack = kilometerStack.addStack(); + unitStack.setPadding(5, 0, 0, 0); + const oilWasteUnit = unitStack.addText('km'); + oilWasteUnit.font = Font.boldSystemFont(14); + oilWasteUnit.textColor = this.widgetColor; + kilometerStack.addSpacer(); + + stack.addSpacer(); + + const btBodyStack = stack.addStack(); + btBodyStack.addSpacer(); + const bottomStack = btBodyStack.addStack(); + bottomStack.setPadding(10, 0, 10, 0); + bottomStack.centerAlignContent(); + bottomStack.addSpacer(); + bottomStack.cornerRadius = 15; + bottomStack.backgroundColor = this.viewColor; + const dataTime = this.dataSource.remoteInfo.datatime; + const countKmText = bottomStack.addText(`上传:${dataTime || '-'}`); + countKmText.textColor = this.widgetColor; + countKmText.font = Font.boldSystemFont(12); + countKmText.centerAlignText(); + bottomStack.addSpacer(); + w.addSpacer(); + return w; + }; + + renderLarge = async (w) => { + return this.renderSmall(w); + }; + + renderMedium = async (w) => { + const containerStack = w.addStack(); + containerStack.centerAlignContent(); + const carStack = containerStack.addStack(); + carStack.addSpacer(); + carStack.backgroundColor = this.viewColor; + + carStack.layoutVertically(); + + carStack.centerAlignContent(); + carStack.size = new Size(this.widgetHeight, this.widgetHeight); + carStack.cornerRadius = 20; + const carImg = await this.renderImage(this.serveInfo.picUrl); + + const carImgStack = carStack.addStack(); + const carResStack = carImgStack.addImage(carImg); + carResStack.imageSize = new Size(137.5, 70); + + carStack.addSpacer(); + + const carNumberStack = carStack.addStack(); + carNumberStack.addSpacer(); + carNumberStack.centerAlignContent(); + const carNumberText = carNumberStack.addText(this.serveInfo.carNumber); + carNumberText.font = Font.boldSystemFont(24); + carNumberText.textColor = this.widgetColor; + carNumberText.centerAlignText(); + carNumberStack.addSpacer(); + + carStack.addSpacer(); + + const carSafeStack = carStack.addStack(); + carSafeStack.addSpacer(); + carSafeStack.centerAlignContent(); + + let safeIconImg; + if (this.dataSource.safeText) { + safeIconImg = carSafeStack.addImage(SFSymbol.named('lock.open').image); + } else { + safeIconImg = carSafeStack.addImage(SFSymbol.named('lock').image); + } + + carSafeStack.addSpacer(5); + const statusText = carSafeStack.addText( + this.dataSource.safeText || '已上锁' + ); + statusText.centerAlignText(); + statusText.font = Font.systemFont(12); + statusText.textColor = this.dataSource.safeText + ? new Color('#f5222d') + : this.widgetColor; + + safeIconImg.tintColor = statusText.textColor; + safeIconImg.imageSize = new Size(10, 14); + + carSafeStack.addSpacer(); + + carStack.addSpacer(); + + containerStack.addSpacer(); + const rightStack = containerStack.addStack(); + rightStack.layoutVertically(); + await this.renderSmall(rightStack); + + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(10, 10, 10, 10); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else { + return await this.notSupport(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/ChinaTelecom.js b/Scripts/ChinaTelecom.js index 10a99ed..03fa63f 100644 --- a/Scripts/ChinaTelecom.js +++ b/Scripts/ChinaTelecom.js @@ -3,30 +3,30 @@ // icon-color: pink; icon-glyph: paper-plane; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === 'undefined') require = importModule; -const {DmYY, Runing} = require('./DmYY'); +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = '中国电信'; - this.en = 'ChinaTelecom'; + this.name = "中国电信"; + this.en = "ChinaTelecom"; this.Run(); } - cookie = ''; - authToken = ''; - fgCircleColor = Color.dynamic(new Color('#dddef3'), new Color('#fff')); + cookie = ""; + authToken = ""; + fgCircleColor = Color.dynamic(new Color("#dddef3"), new Color("#fff")); percentColor = this.widgetColor; - textColor1 = Color.dynamic(new Color('#333'), new Color('#fff')); + textColor1 = Color.dynamic(new Color("#333"), new Color("#fff")); textColor2 = this.widgetColor; - circleColor1 = new Color('#ffbb73'); - circleColor2 = new Color('#ff0029'); - circleColor3 = new Color('#00b800'); - circleColor4 = new Color('#8376f9'); - iconColor = new Color('#827af1'); + circleColor1 = new Color("#ffbb73"); + circleColor2 = new Color("#ff0029"); + circleColor3 = new Color("#00b800"); + circleColor4 = new Color("#8376f9"); + iconColor = new Color("#827af1"); format = (str) => { return parseInt(str) >= 10 ? str : `0${str}`; @@ -43,37 +43,37 @@ class Widget extends DmYY { // percent 的计算方式,剩余/总量 * 100 = 百分比| 百分比 * 3.6 ,为显示进度。 phoneBill = { percent: 0, - label: '话费剩余', + label: "话费剩余", count: 0, - unit: '元', - icon: 'yensign.circle', + unit: "元", + icon: "yensign.circle", circleColor: this.circleColor1, }; flow = { percent: 0, - label: '流量剩余', + label: "流量剩余", count: 0, - unit: 'M', - icon: 'waveform.path.badge.minus', + unit: "M", + icon: "waveform.path.badge.minus", circleColor: this.circleColor2, }; voice = { percent: 0, - label: '语音剩余', + label: "语音剩余", count: 0, - unit: '分钟', - icon: 'mic', + unit: "分钟", + icon: "mic", circleColor: this.circleColor3, }; updateTime = { percent: 0, - label: '电信更新', + label: "电信更新", count: `${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, - unit: '', - urlIcon: 'https://raw.githubusercontent.com/Orz-3/mini/master/10000.png', + unit: "", + urlIcon: "https://raw.githubusercontent.com/Orz-3/mini/master/10000.png", circleColor: this.circleColor4, }; @@ -89,12 +89,12 @@ class Widget extends DmYY { // "User-Agent": "TYUserCenter/2.8 (iPhone; iOS 14.0; Scale/3.00)", }, // body: "t=tysuit", - method: 'POST', + method: "POST", }; fetchUri = { - detail: 'https://e.189.cn/store/user/package_detail.do', - balance: 'https://e.189.cn/store/user/balance_new.do', + detail: "https://e.189.cn/store/user/package_detail.do", + balance: "https://e.189.cn/store/user/balance_new.do", }; init = async () => { @@ -112,9 +112,9 @@ class Widget extends DmYY { formatFlow(number) { const n = number / 1024; if (n < 1024) { - return {count: n.toFixed(2), unit: 'M'}; + return { count: n.toFixed(2), unit: "M" }; } - return {count: (n / 1024).toFixed(2), unit: 'G'}; + return { count: (n / 1024).toFixed(2), unit: "G" }; } getData = async () => { @@ -131,7 +131,7 @@ class Widget extends DmYY { if (detail.result === 0) { // 套餐分钟数 this.voice.percent = Math.floor( - (parseInt(detail.voiceBalance) / parseInt(detail.voiceAmount)) * 100, + (parseInt(detail.voiceBalance) / parseInt(detail.voiceAmount)) * 100 ); this.voice.count = detail.voiceBalance; console.log(detail.items); @@ -139,10 +139,10 @@ class Widget extends DmYY { detail.items.forEach((data) => { if (data.offerType !== 19) { data.items.forEach((item) => { - if (item.unitTypeId === '3') { - if (item.usageAmount !== '0' && item.balanceAmount !== '0') { + if (item.unitTypeId === "3") { + if (item.usageAmount !== "0" && item.balanceAmount !== "0") { this.flow.percent = Math.floor( - (item.balanceAmount / (item.ratableAmount || 1)) * 100, + (item.balanceAmount / (item.ratableAmount || 1)) * 100 ); const flow = this.formatFlow(item.balanceAmount); this.flow.count = flow.count; @@ -155,19 +155,19 @@ class Widget extends DmYY { }); } else { this.flow.percent = Math.floor( - (detail.balance / (detail.total || 1)) * 100, + (detail.balance / (detail.total || 1)) * 100 ); const flow = this.formatFlow(detail.balance); this.flow.count = flow.count; this.flow.unit = flow.unit; this.flow.max = detail.total; } - } if (balance.result === 0) { // 余额 this.phoneBill.count = parseFloat( - parseInt(balance.totalBalanceAvailable) / 100).toFixed(2) + parseInt(balance.totalBalanceAvailable) / 100 + ).toFixed(2); } this.phoneBill.percent = Math.floor((this.phoneBill.count / 100) * 100); }; @@ -194,13 +194,13 @@ class Widget extends DmYY { canvas.setFillColor(color); for (let t = 0; t < degree; t++) { const rect_x = - ctr.x + - (this.canvRadius - radiusOffset) * this.sinDeg(t) - - this.canvWidth / 2; + ctr.x + + (this.canvRadius - radiusOffset) * this.sinDeg(t) - + this.canvWidth / 2; const rect_y = - ctr.y - - (this.canvRadius - radiusOffset) * this.cosDeg(t) - - this.canvWidth / 2; + ctr.y - + (this.canvRadius - radiusOffset) * this.cosDeg(t) - + this.canvWidth / 2; const rect_r = new Rect(rect_x, rect_y, this.canvWidth, this.canvWidth); canvas.fillEllipse(rect_r); } @@ -208,10 +208,10 @@ class Widget extends DmYY { drawText(txt, canvas, txtOffset, fontSize) { const txtRect = new Rect( - this.canvTextSize / 2 - 20, - txtOffset - this.canvTextSize / 2, - this.canvSize, - this.canvTextSize, + this.canvTextSize / 2 - 20, + txtOffset - this.canvTextSize / 2, + this.canvSize, + this.canvTextSize ); canvas.setTextColor(this.percentColor); canvas.setFont(Font.boldSystemFont(fontSize)); @@ -238,10 +238,10 @@ class Widget extends DmYY { const canvas = this.makeCanvas(); stackCircle.size = new Size(70, 70); this.makeCircle( - canvas, - this.dayRadiusOffset, - data.percent * 3.6, - data.circleColor, + canvas, + this.dayRadiusOffset, + data.percent * 3.6, + data.circleColor ); this.drawText(data.percent, canvas, 75, 18); @@ -251,8 +251,8 @@ class Widget extends DmYY { stackCircle.setPadding(20, 0, 0, 0); stackCircle.addSpacer(); const icon = data.urlIcon - ? {image: data.icon} - : SFSymbol.named(data.icon); + ? { image: data.icon } + : SFSymbol.named(data.icon); const imageIcon = stackCircle.addImage(icon.image); imageIcon.tintColor = this.iconColor; imageIcon.imageSize = new Size(15, 15); @@ -293,11 +293,11 @@ class Widget extends DmYY { const stackFooter = stackBody.addStack(); stackFooter.addSpacer(); const text = this.textFormat.defaultText; - text.color = new Color('#aaa'); + text.color = new Color("#aaa"); this.provideText( - `电信更新:${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, - stackFooter, - text, + `电信更新:${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, + stackFooter, + text ); stackFooter.addSpacer(); return w; @@ -312,8 +312,8 @@ class Widget extends DmYY { const stackBottom = stackBody.addStack(); this.setCircleText(stackBottom, this.voice); this.updateTime.icon = await this.$request.get( - this.updateTime.urlIcon, - 'IMG', + this.updateTime.urlIcon, + "IMG" ); this.setCircleText(stackBottom, this.updateTime); return w; @@ -325,18 +325,18 @@ class Widget extends DmYY { renderWebView = async () => { const webView = new WebView(); - const url = 'https://e.189.cn/index.do'; + const url = "https://e.189.cn/index.do"; await webView.loadURL(url); await webView.present(false); const request = new Request(this.fetchUri.detail); - request.method = 'POST'; + request.method = "POST"; const response = await request.loadJSON(); console.log(response); if (response.result === -10001) { - const index = await this.generateAlert('未获取到用户信息', [ - '取消', - '重试', + const index = await this.generateAlert("未获取到用户信息", [ + "取消", + "重试", ]); if (index === 0) return; await this.renderWebView(); @@ -344,7 +344,7 @@ class Widget extends DmYY { const cookies = request.response.cookies; let cookie = []; cookie = cookies.map((item) => `${item.name}=${item.value}`); - cookie = cookie.join('; '); + cookie = cookie.join("; "); this.settings.cookie = cookie; this.saveSettings(); } @@ -352,35 +352,45 @@ class Widget extends DmYY { Run() { if (config.runsInApp) { - const widgetInitConfig = {cookie: 'china_telecom_cookie'}; - this.registerAction('颜色配置', async () => { + const widgetInitConfig = { cookie: "china_telecom_cookie" }; + this.registerAction("颜色配置", async () => { await this.setAlertInput( - `${this.name}颜色配置`, - '进度条颜色|底圈颜色\n图标颜色|比值颜色|值颜色', - { - step1: '进度颜色 1', - step2: '进度颜色 2', - step3: '进度颜色 3', - step4: '进度颜色 4', - inner: '底圈颜色', - icon: '图标颜色', - percent: '比值颜色', - value: '值颜色', - }, + `${this.name}颜色配置`, + "进度条颜色|底圈颜色\n图标颜色|比值颜色|值颜色", + { + step1: "进度颜色 1", + step2: "进度颜色 2", + step3: "进度颜色 3", + step4: "进度颜色 4", + inner: "底圈颜色", + icon: "图标颜色", + percent: "比值颜色", + value: "值颜色", + } ); }); - this.registerAction('账号设置', async () => { - const index = await this.generateAlert('设置账号信息', [ - '取消', - '网站登录', + this.registerAction("账号设置", async () => { + const index = await this.generateAlert("设置账号信息", [ + "取消", + "手动输入", + "网站登录", ]); if (index === 0) return; - await this.renderWebView(); + + if (index === 1) + return this.setAlertInput( + `${this.name}账号设置`, + "手动输入电信 COOKIE", + { + cookie: "电信 cookie", + } + ); + return this.renderWebView(); }); - this.registerAction('代理缓存', async () => { + this.registerAction("代理缓存", async () => { await this.setCacheBoxJSData(widgetInitConfig); }); - this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction("基础设置", this.setWidgetConfig); } const { step1, @@ -419,9 +429,9 @@ class Widget extends DmYY { const widget = new ListWidget(); widget.setPadding(0, 0, 0, 0); await this.getWidgetBackgroundImage(widget); - if (this.widgetFamily === 'medium') { + if (this.widgetFamily === "medium") { return await this.renderMedium(widget); - } else if (this.widgetFamily === 'large') { + } else if (this.widgetFamily === "large") { return await this.renderLarge(widget); } else { return await this.renderSmall(widget); diff --git a/Scripts/Contact.js b/Scripts/Contact.js index 9e58caf..9237e34 100644 --- a/Scripts/Contact.js +++ b/Scripts/Contact.js @@ -3,219 +3,239 @@ // icon-color: deep-green; icon-glyph: mobile-alt; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "桌面联系人"; - this.en = "ContactTable"; - this.userName = arg || "Ya"; - this.Run(); - } - - - today = ""; - useBoxJS = false; - dataSource = {}; - phoneNumber = {}; - size1 = new Size(15, 15); - size2 = new Size(30, 30); - - - init = async () => { - try { - const cardAll = await ContactsContainer.all(); - const data = await Contact.all(cardAll); - if (!this.userName) { - this.dataSource = data[0]; - } else { - this.dataSource = data.find(item => { - return (item.familyName === this.userName || - item.givenName === this.userName || - item.nickname === this.userName || - `${item.familyName}${item.givenName}` === this.userName - ); - }); - } - if (!this.dataSource) return this.notify(this.name, "未找到通讯录相关联系人,请重新设置"); - this.userName = `${this.dataSource.familyName}${this.dataSource.givenName}`; - const phoneNumbers = this.dataSource.phoneNumbers; - if (phoneNumbers.length) { - this.phoneNumber = phoneNumbers[0]; - this.phoneNumber.value = this.phoneNumber.value.replaceAll(" ", ""); - } - } catch (e) { - console.log(e); - } - }; - - setAvatar = (w) => { - const stackBody = w.addStack(); - const stackLeft = stackBody.addStack(); - stackLeft.setPadding(10, 10, 10, 0); - stackLeft.layoutVertically(); - stackLeft.addSpacer(); - const stackAvatar = stackLeft.addStack(); - stackAvatar.centerAlignContent(); - stackAvatar.size = new Size(80, 80); - stackAvatar.borderWidth = 7; - stackAvatar.borderColor = new Color("#222", 0.7); - stackAvatar.cornerRadius = 40; - if (this.dataSource.image) { - const imgAvatar = stackAvatar.addImage(this.dataSource.image); - imgAvatar.imageSize = new Size(80, 80); - } else { - let textFormat = this.textFormat.title; - textFormat.color = this.widgetColor; - textFormat.size = 42; - this.provideText(this.userName.substr(0, 1) || "", stackAvatar, textFormat); - } - stackLeft.addSpacer(); - stackBody.addSpacer(5); - return stackBody; - }; - - setContentCenter = (stackBody) => { - const stackCenter = stackBody.addStack(); - stackCenter.setPadding(10, 0, 10, 10); - stackCenter.layoutVertically(); - stackCenter.addSpacer(); - const stackUsername = stackCenter.addStack(); - stackUsername.centerAlignContent(); - stackCenter.addSpacer(15); - const stackPhoneNumber = stackCenter.addStack(); - stackPhoneNumber.centerAlignContent(); - stackCenter.addSpacer(15); - const stackNote = stackCenter.addStack(); - stackNote.centerAlignContent(); - stackCenter.addSpacer(); - - let textFormat = this.textFormat.defaultText; - textFormat.color = this.widgetColor; - textFormat.size = 18; - const phoneNumber = this.phoneNumber.value || ""; - - const iconPerson = SFSymbol.named("person"); - const imgPerson = stackUsername.addImage(iconPerson.image); - imgPerson.tintColor = this.widgetColor; - imgPerson.imageSize = this.size1; - stackUsername.addSpacer(5); - - const iconPhone = SFSymbol.named("iphone"); - const imgIphone = stackPhoneNumber.addImage(iconPhone.image); - imgIphone.tintColor = this.widgetColor; - imgIphone.imageSize = this.size1; - stackPhoneNumber.addSpacer(5); - - const iconNote = SFSymbol.named("envelope"); - const imgNote = stackNote.addImage(iconNote.image); - imgNote.tintColor = this.widgetColor; - imgNote.imageSize = this.size1; - stackNote.addSpacer(5); - - const data = this.dataSource.emailAddresses; - const email = data.length ? data[0] : {}; - this.provideText(this.userName || "", stackUsername, textFormat); - this.provideText(phoneNumber || "", stackPhoneNumber, textFormat); - const mailTextItem = this.provideText(email.value || "", stackNote, textFormat); - mailTextItem.lineLimit = 1; - - stackBody.addSpacer(); - return stackBody; - }; - - stepActionRight = (stackBody) => { - const stackRight = stackBody.addStack(); - stackRight.setPadding(10, 20, 10, 20); - stackRight.layoutVertically(); - stackRight.backgroundColor = this.widgetOpacityColor; - - stackRight.addSpacer(); - const stackCallPhone = stackRight.addStack(); - stackRight.addSpacer(); - const stackSendMessage = stackRight.addStack(); - stackRight.addSpacer(); - const stackDetail = stackRight.addStack(); - stackRight.addSpacer(); - - const phone = this.phoneNumber.value || ""; - const data = this.dataSource.emailAddresses; - const email = data.length ? data[0] : {}; - - stackCallPhone.url = `tel:${phone}`; - stackSendMessage.url = `sms:${phone}`; - stackDetail.url = `mailto:${email.value || ""}`; - - const iconVideo = SFSymbol.named("video"); - const imgVideo = stackCallPhone.addImage(iconVideo.image); - imgVideo.tintColor = this.backGroundColor; - imgVideo.imageSize = this.size2; - - const iconMessage = SFSymbol.named("message"); - const imgMessage = stackSendMessage.addImage(iconMessage.image); - imgMessage.tintColor = this.backGroundColor; - imgMessage.imageSize = this.size2; - - const iconEnvelope = SFSymbol.named("envelope.open"); - const imgEnvelope = stackDetail.addImage(iconEnvelope.image); - imgEnvelope.tintColor = this.backGroundColor; - imgEnvelope.imageSize = this.size2; - - return stackBody; - }; - - renderSmall = (w) => { - this.setContentCenter(stackBody); - this.stepActionRight(stackBody); - return w; - }; - - renderLarge = (w) => { - const stackBody = this.setAvatar(w); - this.setContentCenter(stackBody); - this.stepActionRight(stackBody); - return w; - }; - - renderMedium = (w) => { - const stackBody = this.setAvatar(w); - this.setContentCenter(stackBody); - this.stepActionRight(stackBody); - return w; - }; - - Run() { - if (config.runsInApp) { - this.registerAction("右侧透明", async () => { - await this.setAlertInput("右侧透明", "若不需要右侧背景设置透明度 0 即可", { rightOpacity: "透明度 0~1" }); - }); - this.registerAction("基础设置", this.setWidgetConfig); - } - const light = new Color(this.settings.lightColor, parseInt(this.settings.rightOpacity || 1)); - const dark = new Color(this.settings.darkColor, parseInt(this.settings.rightOpacity || 1)); - this.widgetOpacityColor = Color.dynamic(light, dark); - } - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - widget.setPadding(0, 0, 0, 0); - await this.getWidgetBackgroundImage(widget); - if (this.widgetFamily === "medium") { - return await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - return await this.renderLarge(widget); - } else { - return await this.renderSmall(widget); - } - } + constructor(arg) { + super(arg); + this.name = '桌面联系人'; + this.en = 'ContactTable'; + this.userName = arg || 'Ya'; + this.Run(); + } + + today = ''; + useBoxJS = false; + dataSource = {}; + phoneNumber = {}; + size1 = new Size(15, 15); + size2 = new Size(30, 30); + + init = async () => { + try { + const cardAll = await ContactsContainer.all(); + const data = await Contact.all(cardAll); + if (!this.userName) { + this.dataSource = data[0]; + } else { + this.dataSource = data.find((item) => { + return ( + item.familyName === this.userName || + item.givenName === this.userName || + item.nickname === this.userName || + `${item.familyName}${item.givenName}` === this.userName + ); + }); + } + if (!this.dataSource) + return this.notify(this.name, '未找到通讯录相关联系人,请重新设置'); + this.userName = `${this.dataSource.familyName}${this.dataSource.givenName}`; + const phoneNumbers = this.dataSource.phoneNumbers; + if (phoneNumbers.length) { + this.phoneNumber = phoneNumbers[0]; + this.phoneNumber.value = this.phoneNumber.value.replaceAll(' ', ''); + } + } catch (e) { + console.log(e); + } + }; + + setAvatar = (w) => { + const stackBody = w.addStack(); + const stackLeft = stackBody.addStack(); + stackLeft.setPadding(10, 10, 10, 0); + stackLeft.layoutVertically(); + stackLeft.addSpacer(); + const stackAvatar = stackLeft.addStack(); + stackAvatar.centerAlignContent(); + stackAvatar.size = new Size(80, 80); + stackAvatar.borderWidth = 7; + stackAvatar.borderColor = new Color('#222', 0.7); + stackAvatar.cornerRadius = 40; + if (this.dataSource.image) { + const imgAvatar = stackAvatar.addImage(this.dataSource.image); + imgAvatar.imageSize = new Size(80, 80); + } else { + let textFormat = this.textFormat.title; + textFormat.color = this.widgetColor; + textFormat.size = 42; + this.provideText( + this.userName.substr(0, 1) || '', + stackAvatar, + textFormat + ); + } + stackLeft.addSpacer(); + stackBody.addSpacer(5); + return stackBody; + }; + + setContentCenter = (stackBody) => { + const stackCenter = stackBody.addStack(); + stackCenter.setPadding(10, 0, 10, 10); + stackCenter.layoutVertically(); + stackCenter.addSpacer(); + const stackUsername = stackCenter.addStack(); + stackUsername.centerAlignContent(); + stackCenter.addSpacer(15); + const stackPhoneNumber = stackCenter.addStack(); + stackPhoneNumber.centerAlignContent(); + stackCenter.addSpacer(15); + const stackNote = stackCenter.addStack(); + stackNote.centerAlignContent(); + stackCenter.addSpacer(); + + let textFormat = this.textFormat.defaultText; + textFormat.color = this.widgetColor; + textFormat.size = 18; + const phoneNumber = this.phoneNumber.value || ''; + + const iconPerson = SFSymbol.named('person'); + const imgPerson = stackUsername.addImage(iconPerson.image); + imgPerson.tintColor = this.widgetColor; + imgPerson.imageSize = this.size1; + stackUsername.addSpacer(5); + + const iconPhone = SFSymbol.named('iphone'); + const imgIphone = stackPhoneNumber.addImage(iconPhone.image); + imgIphone.tintColor = this.widgetColor; + imgIphone.imageSize = this.size1; + stackPhoneNumber.addSpacer(5); + + const iconNote = SFSymbol.named('envelope'); + const imgNote = stackNote.addImage(iconNote.image); + imgNote.tintColor = this.widgetColor; + imgNote.imageSize = this.size1; + stackNote.addSpacer(5); + + const data = this.dataSource.emailAddresses; + const email = data.length ? data[0] : {}; + this.provideText(this.userName || '', stackUsername, textFormat); + this.provideText(phoneNumber || '', stackPhoneNumber, textFormat); + const mailTextItem = this.provideText( + email.value || '', + stackNote, + textFormat + ); + mailTextItem.lineLimit = 1; + + stackBody.addSpacer(); + return stackBody; + }; + + stepActionRight = (stackBody) => { + const stackRight = stackBody.addStack(); + stackRight.setPadding(10, 20, 10, 20); + stackRight.layoutVertically(); + stackRight.backgroundColor = this.widgetOpacityColor; + + stackRight.addSpacer(); + const stackCallPhone = stackRight.addStack(); + stackRight.addSpacer(); + const stackSendMessage = stackRight.addStack(); + stackRight.addSpacer(); + const stackDetail = stackRight.addStack(); + stackRight.addSpacer(); + + const phone = this.phoneNumber.value || ''; + const data = this.dataSource.emailAddresses; + const email = data.length ? data[0] : {}; + + stackCallPhone.url = `tel:${phone}`; + stackSendMessage.url = `sms:${phone}`; + stackDetail.url = `mailto:${email.value || ''}`; + + const iconVideo = SFSymbol.named('video'); + const imgVideo = stackCallPhone.addImage(iconVideo.image); + imgVideo.tintColor = this.backGroundColor; + imgVideo.imageSize = this.size2; + + const iconMessage = SFSymbol.named('message'); + const imgMessage = stackSendMessage.addImage(iconMessage.image); + imgMessage.tintColor = this.backGroundColor; + imgMessage.imageSize = this.size2; + + const iconEnvelope = SFSymbol.named('envelope.open'); + const imgEnvelope = stackDetail.addImage(iconEnvelope.image); + imgEnvelope.tintColor = this.backGroundColor; + imgEnvelope.imageSize = this.size2; + + return stackBody; + }; + + renderSmall = (w) => { + this.setContentCenter(stackBody); + this.stepActionRight(stackBody); + return w; + }; + + renderLarge = (w) => { + const stackBody = this.setAvatar(w); + this.setContentCenter(stackBody); + this.stepActionRight(stackBody); + return w; + }; + + renderMedium = (w) => { + const stackBody = this.setAvatar(w); + this.setContentCenter(stackBody); + this.stepActionRight(stackBody); + return w; + }; + + Run() { + if (config.runsInApp) { + this.registerAction({ + icon: { name: 'phone', color: '#722ed1' }, + type: 'input', + title: '右侧透明', + desc: '若不需要右侧背景设置透明度 0 即可', + placeholder: '透明度 0~1', + val: 'rightOpacity', + }); + + this.registerAction('基础设置', this.setWidgetConfig); + } + const light = new Color( + this.settings.lightColor, + parseInt(this.settings.rightOpacity || 1) + ); + const dark = new Color( + this.settings.darkColor, + parseInt(this.settings.rightOpacity || 1) + ); + this.widgetOpacityColor = Color.dynamic(light, dark); + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(0, 0, 0, 0); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } } // @组件代码结束 diff --git a/Scripts/DmYY.js b/Scripts/DmYY.js index db2b6b6..04bee30 100644 --- a/Scripts/DmYY.js +++ b/Scripts/DmYY.js @@ -5,41 +5,63 @@ /* * Author: 2Ya * Github: https://github.com/dompling + * UI 配置升级 感谢 @LSP 大佬提供代码 */ class DmYY { - constructor(arg) { + constructor(arg, defaultSettings) { this.arg = arg; - this._actions = {}; - this.init(); + this.defaultSettings = defaultSettings || {}; + this.SETTING_KEY = this.md5(Script.name()); + this._init(); this.isNight = Device.isUsingDarkAppearance(); } - BACKGROUND_NIGHT_KEY; + BaseCacheKey = 'DmYY'; + _actions = []; + _menuActions = []; widgetColor; backGroundColor; - useBoxJS = true; isNight; + userConfigKey = ['avatar', 'nickname', 'homePageDesc']; + // 获取 Request 对象 getRequest = (url = '') => { return new Request(url); }; // 发起请求 - http = async (options = { headers: {}, url: '' }, type = 'JSON') => { + http = async ( + options = { headers: {}, url: '' }, + type = 'JSON', + onError = () => { + return SFSymbol.named('photo').image; + } + ) => { + let request; try { - let request; - if (type !== 'IMG') { - request = this.getRequest(); - Object.keys(options).forEach((key) => { - request[key] = options[key]; - }); - request.headers = { ...this.defaultHeaders, ...options.headers }; - } else { + if (type === 'IMG') { + const fileName = `${this.cacheImage}/${this.md5(options.url)}`; request = this.getRequest(options.url); - return (await request.loadImage()) || SFSymbol.named('photo').image; + let response; + if (await this.FILE_MGR.fileExistsExtra(fileName)) { + request.loadImage().then((res) => { + this.FILE_MGR.writeImage(fileName, res); + }); + return Image.fromFile(fileName); + } else { + response = await request.loadImage(); + this.FILE_MGR.writeImage(fileName, response); + } + return response; } + request = this.getRequest(); + Object.keys(options).forEach((key) => { + request[key] = options[key]; + }); + request.headers = { ...this.defaultHeaders, ...options.headers }; + if (type === 'JSON') { return await request.loadJSON(); } @@ -49,13 +71,13 @@ class DmYY { return await request.loadJSON(); } catch (e) { console.log('error:' + e); - if (type === 'IMG') return SFSymbol.named('photo').image; + if (type === 'IMG') return onError?.(); } }; //request 接口请求 $request = { - get: async (url = '', options = {}, type = 'JSON') => { + get: (url = '', options = {}, type = 'JSON') => { let params = { ...options, method: 'GET' }; if (typeof url === 'object') { params = { ...params, ...url }; @@ -64,9 +86,9 @@ class DmYY { } let _type = type; if (typeof options === 'string') _type = options; - return await this.http(params, _type); + return this.http(params, _type); }, - post: async (url = '', options = {}, type = 'JSON') => { + post: (url = '', options = {}, type = 'JSON') => { let params = { ...options, method: 'POST' }; if (typeof url === 'object') { params = { ...params, ...url }; @@ -75,20 +97,39 @@ class DmYY { } let _type = type; if (typeof options === 'string') _type = options; - return await this.http(params, _type); + return this.http(params, _type); }, }; // 获取 boxJS 缓存 - getCache = async (key) => { + getCache = async (key = '', notify = true) => { try { - const url = 'http://' + this.prefix + '/query/boxdata'; - const boxdata = await this.$request.get(url); - console.log(boxdata.datas[key]); - if (key) return boxdata.datas[key]; + let url = 'http://' + this.prefix + '/query/boxdata'; + if (key) url = 'http://' + this.prefix + '/query/data/' + key; + const boxdata = await this.$request.get( + url, + key ? { timeoutInterval: 1 } : {} + ); + if (key) { + this.settings.BoxJSData = { + ...this.settings.BoxJSData, + [key]: boxdata.val, + }; + this.saveSettings(false); + } + if (boxdata.val) return boxdata.val; + return boxdata.datas; } catch (e) { - console.log(e); + if (key && this.settings.BoxJSData[key]) { + return this.settings.BoxJSData[key]; + } + if (notify) + await this.notify( + `${this.name} - BoxJS 数据读取失败`, + '请检查 BoxJS 域名是否为代理复写的域名,如(boxjs.net 或 boxjs.com)。\n若没有配置 BoxJS 相关模块,请点击通知查看教程', + 'https://chavyleung.gitbook.io/boxjs/awesome/videos' + ); return false; } }; @@ -106,13 +147,21 @@ class DmYY { }; // 选择图片并缓存 - chooseImg = async () => { - return await Photos.fromLibrary(); + chooseImg = async (verify = false) => { + const response = await Photos.fromLibrary().catch((err) => { + console.log('图片选择异常:' + err); + }); + if (verify) { + const bool = await this.verifyImage(response); + if (bool) return response; + return null; + } + return response; }; // 设置 widget 背景图片 getWidgetBackgroundImage = async (widget) => { - const backgroundImage = this.getBackgroundImage(); + const backgroundImage = await this.getBackgroundImage(); if (backgroundImage) { const opacity = Device.isUsingDarkAppearance() ? Number(this.settings.darkOpacity) @@ -120,7 +169,7 @@ class DmYY { widget.backgroundImage = await this.shadowImage( backgroundImage, '#000', - opacity, + opacity ); return true; } else { @@ -137,32 +186,28 @@ class DmYY { * 验证图片尺寸: 图片像素超过 1000 左右的时候会导致背景无法加载 * @param img Image */ - verifyImage = async (img) => { - try { - const { width, height } = img.size; - const direct = true; - if (width > 1000) { - const options = ['取消', '打开图像处理']; - const message = - '您的图片像素为' + - width + - ' x ' + - height + - '\n' + - '请将图片' + - (direct ? '宽度' : '高度') + - '调整到 1000 以下\n' + - (!direct ? '宽度' : '高度') + - '自动适应'; - const index = await this.generateAlert(message, options); - if (index === 1) - Safari.openInApp('https://www.sojson.com/image/change.html', false); - return false; - } - return true; - } catch (e) { + verifyImage = async (img = {}) => { + const { width, height } = img.size; + const direct = true; + if (width > 1000) { + const options = ['取消', '打开图像处理']; + const message = + '您的图片像素为' + + width + + ' x ' + + height + + '\n' + + '请将图片' + + (direct ? '宽度' : '高度') + + '调整到 1000 以下\n' + + (!direct ? '宽度' : '高度') + + '自动适应'; + const index = await this.generateAlert(message, options); + if (index === 1) + Safari.openInApp('https://www.sojson.com/image/change.html', false); return false; } + return true; }; /** @@ -182,10 +227,146 @@ class DmYY { return draw.getImage(); } - // Pixel sizes and positions for widgets on all supported phones. - function phoneSizes() { + function phoneSizes(inputHeight) { return { - // 12 Pro Max + /* + + Supported devices + ================= + The following device measurements have been confirmed in iOS 18. + + */ + + // 16 Pro Max + 2868: { + text: { + small: 510, + medium: 1092, + large: 1146, + left: 114, + right: 696, + top: 276, + middle: 912, + bottom: 1548, + }, + notext: { + small: 530, + medium: 1138, + large: 1136, + left: 91, + right: 699, + top: 276, + middle: 882, + bottom: 1488, + }, + }, + + // 16 Plus, 15 Plus, 15 Pro Max, 14 Pro Max + 2796: { + text: { + small: 510, + medium: 1092, + large: 1146, + left: 98, + right: 681, + top: 252, + middle: 888, + bottom: 1524, + }, + notext: { + small: 530, + medium: 1139, + large: 1136, + left: 75, + right: 684, + top: 252, + middle: 858, + bottom: 1464, + }, + }, + + // 16 Pro + 2622: { + text: { + small: 486, + medium: 1032, + large: 1098, + left: 87, + right: 633, + top: 261, + middle: 872, + bottom: 1485, + }, + notext: { + small: 495, + medium: 1037, + large: 1035, + left: 84, + right: 626, + top: 270, + middle: 810, + bottom: 1350, + }, + }, + + // 16, 15, 15 Pro, 14 Pro + 2556: { + text: { + small: 474, + medium: 1017, + large: 1062, + left: 81, + right: 624, + top: 240, + middle: 828, + bottom: 1416, + }, + notext: { + small: 495, + medium: 1047, + large: 1047, + left: 66, + right: 618, + top: 243, + middle: 795, + bottom: 1347, + }, + }, + + // SE3, SE2 + 1334: { + text: { + small: 296, + medium: 642, + large: 648, + left: 54, + right: 400, + top: 60, + middle: 412, + bottom: 764, + }, + notext: { + small: 309, + medium: 667, + large: 667, + left: 41, + right: 399, + top: 67, + middle: 425, + bottom: 783, + }, + }, + + /* + + In-limbo devices + ================= + The following device measurements were confirmed in older versions of iOS. + Please comment if you can confirm these for iOS 18. + + */ + + // 14 Plus, 13 Pro Max, 12 Pro Max 2778: { small: 510, medium: 1092, @@ -197,7 +378,19 @@ class DmYY { bottom: 1518, }, - // 12 and 12 Pro + // 11 Pro Max, XS Max + 2688: { + small: 507, + medium: 1080, + large: 1137, + left: 81, + right: 654, + top: 228, + middle: 858, + bottom: 1488, + }, + + // 14, 13, 13 Pro, 12, 12 Pro 2532: { small: 474, medium: 1014, @@ -209,16 +402,28 @@ class DmYY { bottom: 1407, }, - // 11 Pro Max, XS Max - 2688: { - small: 507, - medium: 1080, - large: 1137, - left: 81, - right: 654, - top: 228, - middle: 858, - bottom: 1488, + // 13 mini, 12 mini / 11 Pro, XS, X + 2436: { + x: { + small: 465, + medium: 987, + large: 1035, + left: 69, + right: 591, + top: 213, + middle: 783, + bottom: 1353, + }, + mini: { + small: 465, + medium: 987, + large: 1035, + left: 69, + right: 591, + top: 231, + middle: 801, + bottom: 1371, + }, }, // 11, XR @@ -226,26 +431,34 @@ class DmYY { small: 338, medium: 720, large: 758, - left: 54, - right: 436, - top: 160, - middle: 580, - bottom: 1000, + left: 55, + right: 437, + top: 159, + middle: 579, + bottom: 999, }, - // 11 Pro, XS, X - 2436: { - small: 465, - medium: 987, - large: 1035, - left: 69, - right: 591, - top: 213, - middle: 783, - bottom: 1353, + // 11 and XR in Display Zoom mode + 1624: { + small: 310, + medium: 658, + large: 690, + left: 46, + right: 394, + top: 142, + middle: 522, + bottom: 902, }, - // Plus phones + /* + + Older devices + ================= + The following devices cannot be updated to iOS 18 or later. + + */ + + // Home button Plus phones 2208: { small: 471, medium: 1044, @@ -257,16 +470,16 @@ class DmYY { bottom: 1278, }, - // SE2 and 6/6S/7/8 - 1334: { - small: 296, - medium: 642, - large: 648, - left: 54, - right: 400, - top: 60, - middle: 412, - bottom: 764, + // Home button Plus in Display Zoom mode + 2001: { + small: 444, + medium: 963, + large: 972, + left: 81, + right: 600, + top: 90, + middle: 618, + bottom: 1146, }, // SE1 @@ -280,31 +493,7 @@ class DmYY { middle: 399, bottom: 399, }, - - // 11 and XR in Display Zoom mode - 1624: { - small: 310, - medium: 658, - large: 690, - left: 46, - right: 394, - top: 142, - middle: 522, - bottom: 902, - }, - - // Plus in Display Zoom mode - 2001: { - small: 444, - medium: 963, - large: 972, - left: 81, - right: 600, - top: 90, - middle: 618, - bottom: 1146, - }, - }; + }[inputHeight]; } let message = @@ -316,12 +505,41 @@ class DmYY { // Get screenshot and determine phone size. let img = await Photos.fromLibrary(); let height = img.size.height; - let phone = phoneSizes()[height]; + let phone = phoneSizes(height); if (!phone) { message = '好像您选择的照片不是正确的截图,请先前往桌面'; await this.generateAlert(message, ['我已知晓']); return; } + // Extra setup needed for 2436-sized phones. + if (height === 2436) { + const files = this.FILE_MGR_LOCAL; + let cacheName = 'mz-phone-type'; + let cachePath = files.joinPath(files.libraryDirectory(), cacheName); + + // If we already cached the phone size, load it. + if (files.fileExists(cachePath)) { + let typeString = files.readString(cachePath); + phone = phone[typeString]; + // Otherwise, prompt the user. + } else { + message = '您的📱型号是?'; + let types = ['iPhone 12 mini', 'iPhone 11 Pro, XS, or X']; + let typeIndex = await this.generateAlert(message, types); + let type = typeIndex === 0 ? 'mini' : 'x'; + phone = phone[type]; + files.writeString(cachePath, type); + } + } + + // If supported, check whether home screen has text labels or not. + if (phone.text) { + message = '主屏幕是否有文本标签?'; + const textOptions = ['有', '无']; + const _textOptions = ['text', 'notext']; + const textResponse = await this.generateAlert(message, textOptions); + phone = phone[_textOptions[textResponse]]; + } // Prompt for widget size and position. message = '截图中要设置透明背景组件的尺寸类型是?'; @@ -388,18 +606,19 @@ class DmYY { return cropImage(img, new Rect(crop.x, crop.y, crop.w, crop.h)); } - setLightAndDark = async (title, desc, val) => { + setLightAndDark = async (title, desc, val, placeholder = '') => { try { const a = new Alert(); a.title = title; a.message = desc; - a.addTextField('', `${this.settings[val]}`); + a.addTextField(placeholder, `${this.settings[val] || ''}`); a.addAction('确定'); a.addCancelAction('取消'); const id = await a.presentAlert(); - if (id === -1) return; - this.settings[val] = a.textFieldValue(0); + if (id === -1) return false; + this.settings[val] = a.textFieldValue(0) || ''; this.saveSettings(); + return true; } catch (e) { console.log(e); } @@ -425,7 +644,7 @@ class DmYY { if (id === -1) return; const data = {}; Object.keys(opt).forEach((key, index) => { - data[key] = a.textFieldValue(index); + data[key] = a.textFieldValue(index) || ''; }); // 保存到本地 if (isSave) { @@ -435,6 +654,26 @@ class DmYY { return data; }; + setBaseAlertInput = async (title, desc, opt = {}, isSave = true) => { + const a = new Alert(); + a.title = title; + a.message = !desc ? '' : desc; + Object.keys(opt).forEach((key) => { + a.addTextField(opt[key], this.baseSettings[key] || ''); + }); + a.addAction('确定'); + a.addCancelAction('取消'); + const id = await a.presentAlert(); + if (id === -1) return; + const data = {}; + Object.keys(opt).forEach((key, index) => { + data[key] = a.textFieldValue(index) || ''; + }); + // 保存到本地 + if (isSave) return this.saveBaseSettings(data); + return data; + }; + /** * 设置当前项目的 boxJS 缓存 * @param opt key value @@ -457,7 +696,7 @@ class DmYY { this.notify( this.name, 'BoxJS 缓存读取失败!点击查看相关教程', - 'https://chavyleung.gitbook.io/boxjs/awesome/videos', + 'https://chavyleung.gitbook.io/boxjs/awesome/videos' ); } }; @@ -467,217 +706,1055 @@ class DmYY { * @returns {Promise} */ setWidgetConfig = async () => { - const table = new UITable(); - table.showSeparators = true; - await this.setContent(table); - await table.present(); - }; - - async preferences(table, arr, outfit) { - let header = new UITableRow(); - let heading = header.addText(outfit); - heading.titleFont = Font.mediumSystemFont(17); - heading.centerAligned(); - table.addRow(header); - arr.forEach((item) => { - let row = new UITableRow(); - let rowTitle = row.addText(item['title']); - rowTitle.widthWeight = 0.5; - rowTitle.titleFont = Font.systemFont(16); - - let valText = row.addText( - `${this.settings[item.val] || item.val || '💬'}`.toUpperCase(), - ); - const fontSize = !item.val ? 26 : 16; - valText.widthWeight = 0.5; - valText.rightAligned(); - valText.titleColor = Color.blue(); - valText.titleFont = Font.mediumSystemFont(fontSize); - row.dismissOnSelect = false; - - row.onSelect = item.onClick - ? () => item.onClick(item, table) - : async () => { - if (item.type == 'input') { - await this.setLightAndDark( - item['title'], - item['desc'], - item['val'], - ); - } else if (item.type == 'setBackground') { - const backImage = await this.getWidgetScreenShot(); - if (backImage) { - await this.setBackgroundImage(backImage, true); - await this.setBackgroundNightImage(backImage, true); - } - } else if (item.type == 'removeBackground') { - const options = ['取消', '清空']; - const message = '该操作不可逆,会清空所有背景图片!'; - const index = await this.generateAlert(message, options); - if (index === 0) return; - await this.setBackgroundImage(false, true); - await this.setBackgroundNightImage(false, true); - } else { - const backImage = await this.chooseImg(); - if (!backImage || !(await this.verifyImage(backImage))) return; - if (item.type == 'setDayBackground') - await this.setBackgroundImage(backImage, true); - if (item.type == 'setNightBackground') - await this.setBackgroundNightImage(backImage, true); - } - await this.setContent(table); - }; - table.addRow(row); - }); - table.reload(); - } - - async setContent(table) { const basic = [ { + icon: { name: 'arrow.clockwise', color: '#1890ff' }, type: 'input', title: '刷新时间', desc: '刷新时间仅供参考,具体刷新时间由系统判断,单位:分钟', val: 'refreshAfterDate', }, { - type: 'input', - title: '白天背景颜色', - desc: - '请自行去网站上搜寻颜色(Hex 颜色)\n支持渐变色,各颜色之间以英文逗号分隔', - val: 'lightBgColor', - }, - { - type: 'input', - title: '晚上背景颜色', - desc: - '请自行去网站上搜寻颜色(Hex 颜色)\n支持渐变色,各颜色之间以英文逗号分隔', - val: 'darkBgColor', - }, - { - type: 'input', + icon: { name: 'sun.max.fill', color: '#d48806' }, + type: 'color', title: '白天字体颜色', desc: '请自行去网站上搜寻颜色(Hex 颜色)', val: 'lightColor', }, { - type: 'input', + icon: { name: 'moon.stars.fill', color: '#d4b106' }, + type: 'color', title: '晚上字体颜色', desc: '请自行去网站上搜寻颜色(Hex 颜色)', val: 'darkColor', }, ]; - const background = [ - { type: 'setBackground', title: '透明背景设置' }, - { type: 'setDayBackground', title: '白天背景图片' }, - { type: 'setNightBackground', title: '晚上背景图片' }, + + return this.renderAppView([ + { title: '基础设置', menu: basic }, { - type: 'input', - title: '白天蒙层透明', - desc: '完全透明请设置为0', - val: 'lightOpacity', + title: '背景设置', + menu: [ + { + icon: { name: 'photo', color: '#13c2c2' }, + type: 'color', + title: '白天背景颜色', + desc: '请自行去网站上搜寻颜色(Hex 颜色)\n支持渐变色,各颜色之间以英文逗号分隔', + val: 'lightBgColor', + }, + { + icon: { name: 'photo.fill', color: '#52c41a' }, + type: 'color', + title: '晚上背景颜色', + desc: '请自行去网站上搜寻颜色(Hex 颜色)\n支持渐变色,各颜色之间以英文逗号分隔', + val: 'darkBgColor', + }, + ], }, { - type: 'input', - title: '晚上蒙层透明', - desc: '完全透明请设置为0', - val: 'darkOpacity', + menu: [ + { + icon: { name: 'photo.on.rectangle', color: '#fa8c16' }, + name: 'dayBg', + type: 'img', + title: '日间背景', + val: this.cacheImage, + verify: true, + }, + { + icon: { name: 'photo.fill.on.rectangle.fill', color: '#fa541c' }, + name: 'nightBg', + type: 'img', + title: '夜间背景', + val: this.cacheImage, + verify: true, + }, + { + icon: { name: 'text.below.photo', color: '#faad14' }, + type: 'img', + name: 'transparentBg', + title: '透明背景', + val: this.cacheImage, + onClick: async (item, __, previewWebView) => { + const backImage = await this.getWidgetScreenShot(); + if (!backImage || !(await this.verifyImage(backImage))) return; + const cachePath = `${item.val}/${item.name}`; + await this.htmlChangeImage(backImage, cachePath, { + previewWebView, + id: item.name, + }); + }, + }, + ], }, - { type: 'removeBackground', title: '清空背景图片' }, - ]; - const boxjs = { - type: 'input', - title: 'BoxJS 域名', - desc: '', - val: 'boxjsDomain', - }; - if (this.useBoxJS) basic.push(boxjs); - table.removeAllRows(); - let topRow = new UITableRow(); - topRow.height = 60; - let leftText = topRow.addButton('Github'); - leftText.widthWeight = 0.3; - leftText.onTap = async () => { - await Safari.openInApp('https://github.com/dompling/Scriptable'); + { + menu: [ + { + icon: { name: 'record.circle', color: '#722ed1' }, + type: 'input', + title: '日间蒙层', + desc: '完全透明请设置为0', + val: 'lightOpacity', + }, + { + icon: { name: 'record.circle.fill', color: '#eb2f96' }, + type: 'input', + title: '夜间蒙层', + desc: '完全透明请设置为0', + val: 'darkOpacity', + }, + ], + }, + { + menu: [ + { + icon: { name: 'clear', color: '#f5222d' }, + name: 'removeBackground', + title: '清空背景图片', + val: `${this.cacheImage}/`, + onClick: async (_, __, previewWebView) => { + const ids = ['dayBg', 'nightBg', 'transparentBg']; + const options = [ + '清空日间', + '清空夜间', + '清空透明', + `清空全部`, + '取消', + ]; + const message = '该操作不可逆,会清空背景图片!'; + const index = await this.generateAlert(message, options); + if (index === 4) return; + switch (index) { + case 3: + await this.htmlChangeImage(false, `${_.val}${ids[0]}`, { + previewWebView, + id: ids[0], + }); + await this.htmlChangeImage(false, `${_.val}${ids[1]}`, { + previewWebView, + id: ids[1], + }); + await this.htmlChangeImage(false, `${_.val}${ids[2]}`, { + previewWebView, + id: ids[2], + }); + return; + default: + await this.htmlChangeImage(false, `${_.val}${ids[index]}`, { + previewWebView, + id: ids[index], + }); + break; + } + }, + }, + ], + }, + { + title: '重置组件', + menu: [ + { + icon: { name: 'trash', color: '#D85888' }, + title: '重置', + desc: '重置当前组件配置', + name: 'reset', + val: 'reset', + onClick: () => { + this.settings = {}; + this.saveSettings(); + this.reopenScript(); + }, + }, + ], + }, + ]).catch((e) => { + console.log(e); + }); + }; + + drawTableIcon = async ( + icon = 'square.grid.2x2', + color = '#504ED5', + cornerWidth = 42 + ) => { + let sfi = SFSymbol.named('square.grid.2x2'); + try { + sfi = SFSymbol.named(icon); + sfi.applyFont(Font.mediumSystemFont(30)); + } catch (e) { + console.log(`图标(${icon})异常:` + e); + } + const imgData = Data.fromPNG(sfi.image).toBase64String(); + const html = ` + + + + `; + const js = ` + var canvas = document.createElement("canvas"); + var sourceImg = document.getElementById("sourceImg"); + var silhouetteImg = document.getElementById("silhouetteImg"); + var ctx = canvas.getContext('2d'); + var size = sourceImg.width > sourceImg.height ? sourceImg.width : sourceImg.height; + canvas.width = size; + canvas.height = size; + ctx.drawImage(sourceImg, (canvas.width - sourceImg.width) / 2, (canvas.height - sourceImg.height) / 2); + var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); + var pix = imgData.data; + //convert the image into a silhouette + for (var i=0, n = pix.length; i < n; i+= 4){ + //set red to 0 + pix[i] = 255; + //set green to 0 + pix[i+1] = 255; + //set blue to 0 + pix[i+2] = 255; + //retain the alpha value + pix[i+3] = pix[i+3]; + } + ctx.putImageData(imgData,0,0); + silhouetteImg.src = canvas.toDataURL(); + output=canvas.toDataURL() + `; + + let wv = new WebView(); + await wv.loadHTML(html); + const base64Image = await wv.evaluateJavaScript(js); + const iconImage = await new Request(base64Image).loadImage(); + const size = new Size(160, 160); + const ctx = new DrawContext(); + ctx.opaque = false; + ctx.respectScreenScale = true; + ctx.size = size; + const path = new Path(); + const rect = new Rect(0, 0, size.width, size.width); + + path.addRoundedRect(rect, cornerWidth, cornerWidth); + path.closeSubpath(); + ctx.setFillColor(new Color(color)); + ctx.addPath(path); + ctx.fillPath(); + const rate = 36; + const iw = size.width - rate; + const x = (size.width - iw) / 2; + ctx.drawImageInRect(iconImage, new Rect(x, x, iw, iw)); + return ctx.getImage(); + }; + + dismissLoading = (webView) => { + webView.evaluateJavaScript( + "window.dispatchEvent(new CustomEvent('JWeb', { detail: { code: 'finishLoading' } }))", + false + ); + }; + + insertTextByElementId = (webView, elementId, text) => { + const scripts = `document.getElementById("${elementId}_val").innerHTML=\`${text}\`;`; + webView.evaluateJavaScript(scripts, false); + }; + + loadSF2B64 = async ( + icon = 'square.grid.2x2', + color = '#56A8D6', + cornerWidth = 42 + ) => { + const sfImg = await this.drawTableIcon(icon, color, cornerWidth); + return `data:image/png;base64,${Data.fromPNG(sfImg).toBase64String()}`; + }; + + setUserInfo = async () => { + const baseOnClick = async (item, _, previewWebView) => { + const data = await this.setBaseAlertInput(item.title, item.desc, { + [item.val]: item.placeholder, + }); + if (!data) return; + this.insertTextByElementId(previewWebView, item.name, data[item.val]); }; - let centerRow = topRow.addImageAtURL( - 'https://s3.ax1x.com/2021/03/16/6y4oJ1.png', + + return this.renderAppView([ + { + title: '个性设置', + menu: [ + { + icon: { name: 'person', color: '#fa541c' }, + name: this.userConfigKey[0], + title: '首页头像', + type: 'img', + val: this.baseImage, + onClick: async (_, __, previewWebView) => { + const options = ['相册选择', '在线链接', '取消']; + const message = '设置个性化头像'; + const index = await this.generateAlert(message, options); + if (index === 2) return; + const cachePath = `${_.val}/${_.name}`; + switch (index) { + case 0: + const albumOptions = ['选择图片', '清空图片', '取消']; + + const albumIndex = await this.generateAlert('', albumOptions); + if (albumIndex === 2) return; + if (albumIndex === 1) { + await this.htmlChangeImage(false, cachePath, { + previewWebView, + id: _.name, + }); + return; + } + + const backImage = await this.chooseImg(); + if (backImage) { + await this.htmlChangeImage(backImage, cachePath, { + previewWebView, + id: _.name, + }); + } + + break; + case 1: + const data = await this.setBaseAlertInput( + '在线链接', + '首页头像在线链接', + { + avatar: '🔗请输入 URL 图片链接', + } + ); + if (!data) return; + + if (data[_.name] !== '') { + const backImage = await this.$request.get( + data[_.name], + 'IMG' + ); + await this.htmlChangeImage(backImage, cachePath, { + previewWebView, + id: _.name, + }); + } else { + await this.htmlChangeImage(false, cachePath, { + previewWebView, + id: _.name, + }); + } + + break; + default: + break; + } + }, + }, + { + icon: { name: 'pencil', color: '#fa8c16' }, + type: 'input', + title: '首页昵称', + desc: '个性化首页昵称', + placeholder: '👤请输入头像昵称', + val: this.userConfigKey[1], + name: this.userConfigKey[1], + defaultValue: this.baseSettings.nickname, + onClick: baseOnClick, + }, + { + icon: { name: 'lineweight', color: '#a0d911' }, + type: 'input', + title: '首页昵称描述', + desc: '个性化首页昵称描述', + placeholder: '请输入描述', + val: this.userConfigKey[2], + name: this.userConfigKey[2], + defaultValue: this.baseSettings.homePageDesc, + onClick: baseOnClick, + }, + ], + }, + { + menu: [ + { + icon: { name: 'shippingbox', color: '#f7bb10' }, + type: 'input', + title: 'BoxJS 域名', + desc: '设置BoxJS访问域名,如:boxjs.net 或 boxjs.com', + val: 'boxjsDomain', + name: 'boxjsDomain', + placeholder: 'boxjs.net', + defaultValue: this.baseSettings.boxjsDomain, + onClick: baseOnClick, + }, + { + icon: { name: 'clear', color: '#f5222d' }, + title: '恢复默认设置', + name: 'reset', + onClick: async () => { + const options = ['取消', '确定']; + const message = '确定要恢复当前所有配置吗?'; + const index = await this.generateAlert(message, options); + if (index === 1) { + this.settings = {}; + this.baseSettings = {}; + + this.FILE_MGR.remove(this.cacheImage); + + for (const item of this.cacheImageBgPath) { + await this.setBackgroundImage(false, item, false); + } + + this.saveSettings(false); + this.saveBaseSettings(); + await this.notify( + '重置成功', + '请关闭窗口之后,重新运行当前脚本' + ); + this.reopenScript(); + } + }, + }, + ], + }, + ]); + }; + + htmlChangeImage = async (image, path, { previewWebView, id }) => { + const base64Img = await this.setBackgroundImage(image, path, false); + console.log(path); + this.insertTextByElementId( + previewWebView, + id, + base64Img ? `` : '' ); - centerRow.widthWeight = 0.4; - centerRow.centerAligned(); - centerRow.onTap = async () => { - await Safari.open('https://t.me/Scriptable_JS'); + }; + + reopenScript = () => { + Safari.open(`scriptable:///run/${encodeURIComponent(Script.name())}`); + }; + + async renderAppView( + options = [], + renderAvatar = false, + previewWebView = new WebView() + ) { + const settingItemFontSize = 14, + authorNameFontSize = 20, + authorDescFontSize = 12; + // ================== 配置界面样式 =================== + const style = ` + :root { + --color-primary: #007aff; + --divider-color: rgba(60,60,67,0.16); + --card-background: #fff; + --card-radius: 8px; + --list-header-color: rgba(60,60,67,0.6); + } + * { + -webkit-user-select: none; + user-select: none; + } + body { + margin: 10px 0; + -webkit-font-smoothing: antialiased; + font-family: "SF Pro Display","SF Pro Icons","Helvetica Neue","Helvetica","Arial",sans-serif; + accent-color: var(--color-primary); + background: #f6f6f6; + } + .list { + margin: 15px; + } + .list__header { + margin: 0 18px; + color: var(--list-header-color); + font-size: 13px; + } + .list__body { + margin-top: 10px; + background: var(--card-background); + border-radius: var(--card-radius); + overflow: hidden; + } + .form-item-auth { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 4em; + padding: 0.5em 18px; + position: relative; + } + .form-item-auth-name { + margin: 0px 12px; + font-size: ${authorNameFontSize}px; + font-weight: 430; + } + .form-item-auth-desc { + margin: 0px 12px; + font-size: ${authorDescFontSize}px; + font-weight: 400; + } + .form-label-author-avatar { + width: 62px; + height: 62px; + border-radius:50%; + border: 1px solid #F6D377; + } + .form-item, .form-item-switch { + display: flex; + align-items: center; + justify-content: space-between; + font-size: ${settingItemFontSize}px; + font-weight: 400; + min-height: 2.2em; + padding: 0.5em 10px; + position: relative; + } + label > * { + pointer-events: none; + } + .form-label { + display: flex; + align-items: center; + flex-wrap:nowrap + } + .form-label-img { + height: 30px; + } + .form-label-title { + margin-left: 8px; + white-space: nowrap; + } + .bottom-bg { + margin: 30px 15px 15px 15px; + } + .form-item--link .icon-arrow-right { + color: #86868b; + } + + .form-item-right-desc { + font-size: 13px; + color: #86868b; + margin: 0 4px 0 auto; + max-width: 130px; + overflow: hidden; + text-overflow: ellipsis; + display:flex; + align-items: center; + white-space: nowrap; + } + + .form-item-right-desc img{ + width:30px; + height:30px; + border-radius:3px; + } + + .form-item + .form-item::before, + .form-item + .form-item-switch::before, + .form-item-switch + .form-item::before, + .form-item-switch + .form-item-switch::before + { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + border-top: 0.5px solid var(--divider-color); + } + + .form-item input[type="checkbox"] { + width: 2em; + height: 2em; + } + input[type='input'],select,input[type='date'] { + width: 100%; + height: 2.3em; + outline-style: none; + text-align: right; + padding: 0px 10px; + border: 1px solid #ddd; + font-size: 14px; + color: #86868b; + border-radius:4px; + } + input[type='checkbox'][role='switch'] { + position: relative; + display: inline-block; + appearance: none; + width: 40px; + height: 24px; + border-radius: 24px; + background: #ccc; + transition: 0.3s ease-in-out; + } + input[type='checkbox'][role='switch']::before { + content: ''; + position: absolute; + left: 2px; + top: 2px; + width: 20px; + height: 20px; + border-radius: 50%; + background: #fff; + transition: 0.3s ease-in-out; + } + input[type='checkbox'][role='switch']:checked { + background: var(--color-primary); + } + input[type='checkbox'][role='switch']:checked::before { + transform: translateX(16px); + } + .copyright { + display: flex; + align-items: center; + justify-content: space-between; + margin: 15px; + font-size: 10px; + color: #86868b; + } + .copyright a { + color: #515154; + text-decoration: none; + } + .preview.loading { + pointer-events: none; + } + .icon-loading { + display: inline-block; + animation: 1s linear infinite spin; + } + .normal-loading { + display: inline-block; + animation: 20s linear infinite spin; + } + @keyframes spin { + 0% { + transform: rotate(0); + } + 100% { + transform: rotate(1turn); + } + } + @media (prefers-color-scheme: dark) { + :root { + --divider-color: rgba(84,84,88,0.65); + --card-background: #1c1c1e; + --list-header-color: rgba(235,235,245,0.6); + } + body { + background: #000; + color: #fff; + } + }`; + + const js = ` + (() => { + + window.invoke = (code, data) => { + window.dispatchEvent( + new CustomEvent( + 'JBridge', + { detail: { code, data } } + ) + ) + } + + // 切换ico的loading效果 + const toggleIcoLoading = (e) => { + try{ + const target = e.currentTarget + target.classList.add('loading') + const icon = e.currentTarget.querySelector('.iconfont') + const className = icon.className + icon.className = 'iconfont icon-loading' + const listener = (event) => { + const { code } = event.detail + if (code === 'finishLoading') { + target.classList.remove('loading') + icon.className = className + window.removeEventListener('JWeb', listener); + } + } + window.addEventListener('JWeb', listener) + }catch(e){ + for (const loading of document.querySelectorAll('.icon-loading')) { + loading.classList.remove('loading'); + loading.className = "iconfont icon-arrow-right"; + } + } + }; + + for (const btn of document.querySelectorAll('.form-item')) { + btn.addEventListener('click', (e) => { + if(!e.target.id)return; + toggleIcoLoading(e); + invoke(e.target.id); + }) + } + + for (const btn of document.querySelectorAll('.form-item__input')) { + btn.addEventListener('change', (e) => { + if(!e.target.name)return; + invoke(e.target.name,e.target.type==="checkbox"?\`\${e.target.checked}\`: e.target.value); + }) + } + + if(${renderAvatar}){ + document.querySelectorAll('.form-item-auth')[0].addEventListener('click', (e) => { + toggleIcoLoading(e); + invoke("userInfo"); + }) + } + + })()`; + + let configList = ``; + let actionsConfig = []; + + for (const key in options) { + const item = options[key]; + actionsConfig = [...item.menu, ...actionsConfig]; + configList += ` + + ${item.title || ''} + + `; + + for (const menuItem of item.menu) { + let iconBase64 = ``; + if (menuItem.children) { + menuItem.onClick = () => { + return this.renderAppView( + typeof menuItem.children === 'function' + ? menuItem.children() + : menuItem.children + ); + }; + } + if (menuItem.url) { + const imageIcon = await this.http( + { url: menuItem.url }, + 'IMG', + () => { + return this.drawTableIcon('gear'); + } + ); + + if (menuItem.url.indexOf('png') !== -1) { + iconBase64 = `data:image/png;base64,${Data.fromPNG( + imageIcon + ).toBase64String()}`; + } else { + iconBase64 = `data:image/png;base64,${Data.fromJPEG( + imageIcon + ).toBase64String()}`; + } + } else { + const icon = menuItem.icon || {}; + iconBase64 = await this.loadSF2B64(icon.name, icon.color); + } + const idName = menuItem.name || menuItem.val; + + let defaultHtml = ``; + menuItem.defaultValue = + this.settings[idName] || menuItem.defaultValue || ''; + + if (menuItem.type === 'input') { + defaultHtml = menuItem.defaultValue || ''; + } else if (menuItem.type === 'img') { + const cachePath = `${menuItem.val}/${menuItem.name}`; + if (await this.FILE_MGR.fileExistsExtra(cachePath)) { + const imageSrc = `data:image/png;base64,${Data.fromFile( + cachePath + ).toBase64String()}`; + defaultHtml = ``; + } + } else if (menuItem.type === 'select') { + let selectOptions = ''; + menuItem.options.forEach((option) => { + let selected = `selected="selected"`; + selectOptions += `${option}`; + }); + defaultHtml = `${selectOptions}`; + } else if (menuItem.type === 'switch') { + const checked = + menuItem.defaultValue == 'true' ? `checked="checked"` : ''; + defaultHtml += ``; + } else if (menuItem.type) { + defaultHtml = ``; + } + + let addLable = ''; + if (menuItem.type === 'switch' || menuItem.type === 'checkbox') { + addLable = ``; + } else { + addLable = ``; + } + + configList += ` + ${addLable} + + + ${menuItem.title} + + + ${defaultHtml} + + + + `; + } + configList += ``; + } + + let avatarHtml = ''; + if (renderAvatar) { + const cachePath = `${this.baseImage}/${this.userConfigKey[0]}`; + const avatarConfig = { + avatar: `https://avatars.githubusercontent.com/u/23498579?v=4`, + nickname: this.baseSettings[this.userConfigKey[1]] || 'Dompling', + homPageDesc: + this.baseSettings[this.userConfigKey[2]] || + '18岁,来自九仙山的设计师', + }; + + if (await this.FILE_MGR.fileExistsExtra(cachePath)) { + avatarConfig.avatar = `data:image/png;base64,${Data.fromFile( + cachePath + ).toBase64String()}`; + } + + avatarHtml = ` + + + + + + + ${avatarConfig.nickname} + ${avatarConfig.homPageDesc} + + + + 个性化设置 + + + + + + `; + } + + const html = ` + + + + + + + + ${avatarHtml} + ${configList} + + + + `; + + // 预览web + await previewWebView.loadHTML(html); + + const injectListener = async () => { + const event = await previewWebView.evaluateJavaScript( + `(() => { + try { + window.addEventListener( + 'JBridge', + (e)=>{ + completion(JSON.stringify(e.detail||{})) + } + ) + } catch (e) { + alert("预览界面出错:" + e); + throw new Error("界面处理出错: " + e); + return; + } + })()`, + true + ); + + const { code, data } = JSON.parse(event); + try { + const actionItem = actionsConfig.find( + (item) => (item.name || item.val) === code + ); + + if (code === 'userInfo') await this.setUserInfo(); + + if (actionItem) { + const idName = actionItem?.name || actionItem?.val; + if (actionItem?.onClick) { + await actionItem?.onClick?.(actionItem, data, previewWebView); + } else if (actionItem.type == 'input') { + if ( + await this.setLightAndDark( + actionItem['title'], + actionItem['desc'], + idName, + actionItem['placeholder'] + ) + ) + this.insertTextByElementId( + previewWebView, + idName, + this.settings[idName] || '' + ); + } else if (actionItem.type === 'img') { + const cachePath = `${actionItem.val}/${actionItem.name}`; + const options = ['相册选择', '清空图片', '取消']; + const message = '相册图片选择,请选择合适图片大小'; + const index = await this.generateAlert(message, options); + switch (index) { + case 0: + const backImage = await this.chooseImg(actionItem.verify); + if (backImage) { + const cachePath = `${actionItem.val}/${actionItem.name}`; + await this.htmlChangeImage(backImage, cachePath, { + previewWebView, + id: idName, + }); + } + break; + case 1: + await this.htmlChangeImage(false, cachePath, { + previewWebView, + id: idName, + }); + break; + default: + break; + } + } else { + if (data !== undefined) { + this.settings[idName] = data; + this.saveSettings(false); + } + } + } + } catch (error) { + console.log('异常操作:' + error); + } + this.dismissLoading(previewWebView); + injectListener(); }; - let rightText = topRow.addButton('重置所有'); - rightText.widthWeight = 0.3; - rightText.rightAligned(); - rightText.onTap = async () => { - const options = ['取消', '重置']; - const message = - '该操作不可逆,会清空所有组件配置!重置后请重新打开设置菜单。'; - const index = await this.generateAlert(message, options); - if (index === 0) return; - this.settings = {}; - await this.setBackgroundImage(false, false); - this.saveSettings(); + + injectListener().catch((e) => { + console.error(e); + this.dismissLoading(previewWebView); + if (!config.runsInApp) { + this.notify('主界面', `🚫 ${e}`); + } + }); + + previewWebView.present(); + } + + initSFSymbol() { + const named = SFSymbol.named; + SFSymbol.named = (str) => { + const current = named(str); + if (!current) { + console.log(`图标异常,请在文中搜索并替换图标:${str}`); + return named('photo'); + } + return current; }; - table.addRow(topRow); - await this.preferences(table, basic, '基础设置'); - await this.preferences(table, background, '背景图片'); + return SFSymbol; } - init(widgetFamily = config.widgetFamily) { + _init(widgetFamily = config.widgetFamily) { + this.initSFSymbol(); // 组件大小:small,medium,large this.widgetFamily = widgetFamily; - this.SETTING_KEY = this.md5(Script.name()); //用于配置所有的组件相关设置 // 文件管理器 // 提示:缓存数据不要用这个操作,这个是操作源码目录的,缓存建议存放在local temp目录中 - this.FILE_MGR = FileManager[ - module.filename.includes('Documents/iCloud~') ? 'iCloud' : 'local' - ](); - // 本地,用于存储图片等 - this.FILE_MGR_LOCAL = FileManager.local(); - this.BACKGROUND_KEY = this.FILE_MGR_LOCAL.joinPath( - this.FILE_MGR_LOCAL.documentsDirectory(), - 'bg_' + this.SETTING_KEY + '.jpg', + this.FILE_MGR = + FileManager[ + module.filename.includes('Documents/iCloud~') ? 'iCloud' : 'local' + ](); + + this.FILE_MGR.fileExistsExtra = async (filePath) => { + const file = this.FILE_MGR.fileExists(filePath); + if (file) await this.FILE_MGR.downloadFileFromiCloud(filePath); + return file; + }; + + this.cacheImage = this.FILE_MGR.joinPath( + this.FILE_MGR.documentsDirectory(), + `/images/${Script.name()}` ); - this.BACKGROUND_NIGHT_KEY = this.FILE_MGR_LOCAL.joinPath( - this.FILE_MGR_LOCAL.documentsDirectory(), - 'bg_' + this.SETTING_KEY + 'night.jpg', + this.baseImage = this.FILE_MGR.joinPath( + this.FILE_MGR.documentsDirectory(), + `/images/` ); + this.cacheImageBgPath = [ + `${this.cacheImage}/transparentBg`, + `${this.cacheImage}/dayBg`, + `${this.cacheImage}/nightBg`, + `${this.baseImage}/avatar`, + ]; + + if (!this.FILE_MGR.fileExists(this.cacheImage)) { + this.FILE_MGR.createDirectory(this.cacheImage, true); + } + + // 本地,用于存储图片等 + this.FILE_MGR_LOCAL = FileManager.local(); + this.settings = this.getSettings(); + + this.baseSettings = this.getBaseSettings(); + + this.settings = { ...this.defaultSettings, ...this.settings }; + this.settings.lightColor = this.settings.lightColor || '#000000'; this.settings.darkColor = this.settings.darkColor || '#ffffff'; this.settings.lightBgColor = this.settings.lightBgColor || '#ffffff'; this.settings.darkBgColor = this.settings.darkBgColor || '#000000'; - this.settings.boxjsDomain = this.settings.boxjsDomain || 'boxjs.net'; + this.settings.boxjsDomain = this.baseSettings.boxjsDomain || 'boxjs.net'; this.settings.refreshAfterDate = this.settings.refreshAfterDate || '30'; this.settings.lightOpacity = this.settings.lightOpacity || '0.4'; this.settings.darkOpacity = this.settings.darkOpacity || '0.7'; + this.prefix = this.settings.boxjsDomain; - const lightBgColor = this.getColors(this.settings.lightBgColor); - const darkBgColor = this.getColors(this.settings.darkBgColor); - if ( - this.getColors(this.settings.lightBgColor).length > 1 || - this.getColors(this.settings.darkBgColor).length > 1 - ) { - this.backGroundColor = !Device.isUsingDarkAppearance() - ? this.getBackgroundColor(lightBgColor) - : this.getBackgroundColor(darkBgColor); - } else { - this.backGroundColor = Color.dynamic( - new Color(this.settings.lightBgColor), - new Color(this.settings.darkBgColor), - ); - } + config.runsInApp && this.saveSettings(false); + + this.backGroundColor = Color.dynamic( + new Color(this.settings.lightBgColor), + new Color(this.settings.darkBgColor) + ); + + // const lightBgColor = this.getColors(this.settings.lightBgColor); + // const darkBgColor = this.getColors(this.settings.darkBgColor); + // if (lightBgColor.length > 1 || darkBgColor.length > 1) { + // this.backGroundColor = !Device.isUsingDarkAppearance() + // ? this.getBackgroundColor(lightBgColor) + // : this.getBackgroundColor(darkBgColor); + // } else if (lightBgColor.length > 0 && darkBgColor.length > 0) { + // this.backGroundColor = Color.dynamic( + // new Color(this.settings.lightBgColor), + // new Color(this.settings.darkBgColor) + // ); + // } + this.widgetColor = Color.dynamic( new Color(this.settings.lightColor), - new Color(this.settings.darkColor), + new Color(this.settings.darkColor) ); } @@ -703,8 +1780,25 @@ class DmYY { * @param {string} name 操作函数名 * @param {func} func 点击后执行的函数 */ - registerAction(name, func) { - this._actions[name] = func.bind(this); + registerAction(name, func, icon = { name: 'gear', color: '#096dd9' }, type) { + if (typeof name === 'object' && !name.menu) return this._actions.push(name); + if (typeof name === 'object' && name.menu) + return this._menuActions.push(name); + + const action = { + name, + type, + title: name, + onClick: func?.bind(this), + }; + + if (typeof icon === 'string') { + action.url = icon; + } else { + action.icon = icon; + } + + this._actions.push(action); } /** @@ -729,195 +1823,8 @@ class DmYY { * md5 加密字符串 * @param {string} str 要加密成md5的数据 */ - md5(str) { - function d(n, t) { - var r = (65535 & n) + (65535 & t); - return (((n >> 16) + (t >> 16) + (r >> 16)) << 16) | (65535 & r); - } - - function f(n, t, r, e, o, u) { - return d(((c = d(d(t, n), d(e, u))) << (f = o)) | (c >>> (32 - f)), r); - var c, f; - } - - function l(n, t, r, e, o, u, c) { - return f((t & r) | (~t & e), n, t, o, u, c); - } - - function v(n, t, r, e, o, u, c) { - return f((t & e) | (r & ~e), n, t, o, u, c); - } - - function g(n, t, r, e, o, u, c) { - return f(t ^ r ^ e, n, t, o, u, c); - } - - function m(n, t, r, e, o, u, c) { - return f(r ^ (t | ~e), n, t, o, u, c); - } - - function i(n, t) { - var r, e, o, u; - (n[t >> 5] |= 128 << t % 32), (n[14 + (((t + 64) >>> 9) << 4)] = t); - for ( - var c = 1732584193, - f = -271733879, - i = -1732584194, - a = 271733878, - h = 0; - h < n.length; - h += 16 - ) - (c = l((r = c), (e = f), (o = i), (u = a), n[h], 7, -680876936)), - (a = l(a, c, f, i, n[h + 1], 12, -389564586)), - (i = l(i, a, c, f, n[h + 2], 17, 606105819)), - (f = l(f, i, a, c, n[h + 3], 22, -1044525330)), - (c = l(c, f, i, a, n[h + 4], 7, -176418897)), - (a = l(a, c, f, i, n[h + 5], 12, 1200080426)), - (i = l(i, a, c, f, n[h + 6], 17, -1473231341)), - (f = l(f, i, a, c, n[h + 7], 22, -45705983)), - (c = l(c, f, i, a, n[h + 8], 7, 1770035416)), - (a = l(a, c, f, i, n[h + 9], 12, -1958414417)), - (i = l(i, a, c, f, n[h + 10], 17, -42063)), - (f = l(f, i, a, c, n[h + 11], 22, -1990404162)), - (c = l(c, f, i, a, n[h + 12], 7, 1804603682)), - (a = l(a, c, f, i, n[h + 13], 12, -40341101)), - (i = l(i, a, c, f, n[h + 14], 17, -1502002290)), - (c = v( - c, - (f = l(f, i, a, c, n[h + 15], 22, 1236535329)), - i, - a, - n[h + 1], - 5, - -165796510, - )), - (a = v(a, c, f, i, n[h + 6], 9, -1069501632)), - (i = v(i, a, c, f, n[h + 11], 14, 643717713)), - (f = v(f, i, a, c, n[h], 20, -373897302)), - (c = v(c, f, i, a, n[h + 5], 5, -701558691)), - (a = v(a, c, f, i, n[h + 10], 9, 38016083)), - (i = v(i, a, c, f, n[h + 15], 14, -660478335)), - (f = v(f, i, a, c, n[h + 4], 20, -405537848)), - (c = v(c, f, i, a, n[h + 9], 5, 568446438)), - (a = v(a, c, f, i, n[h + 14], 9, -1019803690)), - (i = v(i, a, c, f, n[h + 3], 14, -187363961)), - (f = v(f, i, a, c, n[h + 8], 20, 1163531501)), - (c = v(c, f, i, a, n[h + 13], 5, -1444681467)), - (a = v(a, c, f, i, n[h + 2], 9, -51403784)), - (i = v(i, a, c, f, n[h + 7], 14, 1735328473)), - (c = g( - c, - (f = v(f, i, a, c, n[h + 12], 20, -1926607734)), - i, - a, - n[h + 5], - 4, - -378558, - )), - (a = g(a, c, f, i, n[h + 8], 11, -2022574463)), - (i = g(i, a, c, f, n[h + 11], 16, 1839030562)), - (f = g(f, i, a, c, n[h + 14], 23, -35309556)), - (c = g(c, f, i, a, n[h + 1], 4, -1530992060)), - (a = g(a, c, f, i, n[h + 4], 11, 1272893353)), - (i = g(i, a, c, f, n[h + 7], 16, -155497632)), - (f = g(f, i, a, c, n[h + 10], 23, -1094730640)), - (c = g(c, f, i, a, n[h + 13], 4, 681279174)), - (a = g(a, c, f, i, n[h], 11, -358537222)), - (i = g(i, a, c, f, n[h + 3], 16, -722521979)), - (f = g(f, i, a, c, n[h + 6], 23, 76029189)), - (c = g(c, f, i, a, n[h + 9], 4, -640364487)), - (a = g(a, c, f, i, n[h + 12], 11, -421815835)), - (i = g(i, a, c, f, n[h + 15], 16, 530742520)), - (c = m( - c, - (f = g(f, i, a, c, n[h + 2], 23, -995338651)), - i, - a, - n[h], - 6, - -198630844, - )), - (a = m(a, c, f, i, n[h + 7], 10, 1126891415)), - (i = m(i, a, c, f, n[h + 14], 15, -1416354905)), - (f = m(f, i, a, c, n[h + 5], 21, -57434055)), - (c = m(c, f, i, a, n[h + 12], 6, 1700485571)), - (a = m(a, c, f, i, n[h + 3], 10, -1894986606)), - (i = m(i, a, c, f, n[h + 10], 15, -1051523)), - (f = m(f, i, a, c, n[h + 1], 21, -2054922799)), - (c = m(c, f, i, a, n[h + 8], 6, 1873313359)), - (a = m(a, c, f, i, n[h + 15], 10, -30611744)), - (i = m(i, a, c, f, n[h + 6], 15, -1560198380)), - (f = m(f, i, a, c, n[h + 13], 21, 1309151649)), - (c = m(c, f, i, a, n[h + 4], 6, -145523070)), - (a = m(a, c, f, i, n[h + 11], 10, -1120210379)), - (i = m(i, a, c, f, n[h + 2], 15, 718787259)), - (f = m(f, i, a, c, n[h + 9], 21, -343485551)), - (c = d(c, r)), - (f = d(f, e)), - (i = d(i, o)), - (a = d(a, u)); - return [c, f, i, a]; - } - - function a(n) { - for (var t = '', r = 32 * n.length, e = 0; e < r; e += 8) - t += String.fromCharCode((n[e >> 5] >>> e % 32) & 255); - return t; - } - - function h(n) { - var t = []; - for (t[(n.length >> 2) - 1] = void 0, e = 0; e < t.length; e += 1) - t[e] = 0; - for (var r = 8 * n.length, e = 0; e < r; e += 8) - t[e >> 5] |= (255 & n.charCodeAt(e / 8)) << e % 32; - return t; - } - - function e(n) { - for (var t, r = '0123456789abcdef', e = '', o = 0; o < n.length; o += 1) - (t = n.charCodeAt(o)), - (e += r.charAt((t >>> 4) & 15) + r.charAt(15 & t)); - return e; - } - - function r(n) { - return unescape(encodeURIComponent(n)); - } - - function o(n) { - return a(i(h((t = r(n))), 8 * t.length)); - var t; - } - - function u(n, t) { - return (function (n, t) { - var r, - e, - o = h(n), - u = [], - c = []; - for ( - u[15] = c[15] = void 0, - 16 < o.length && (o = i(o, 8 * n.length)), - r = 0; - r < 16; - r += 1 - ) - (u[r] = 909522486 ^ o[r]), (c[r] = 1549556828 ^ o[r]); - return ( - (e = i(u.concat(h(t)), 512 + 8 * t.length)), a(i(c.concat(e), 640)) - ); - })(r(n), r(t)); - } - - function t(n, t, r) { - return t ? (r ? u(t, n) : e(u(t, n))) : r ? o(n) : e(o(n)); - } - - return t(str); - } + // prettier-ignore + md5(str){function d(n,t){var r=(65535&n)+(65535&t);return(((n>>16)+(t>>16)+(r>>16))<<16)|(65535&r)}function f(n,t,r,e,o,u){return d(((c=d(d(t,n),d(e,u)))<<(f=o))|(c>>>(32-f)),r);var c,f}function l(n,t,r,e,o,u,c){return f((t&r)|(~t&e),n,t,o,u,c)}function v(n,t,r,e,o,u,c){return f((t&e)|(r&~e),n,t,o,u,c)}function g(n,t,r,e,o,u,c){return f(t^r^e,n,t,o,u,c)}function m(n,t,r,e,o,u,c){return f(r^(t|~e),n,t,o,u,c)}function i(n,t){var r,e,o,u;(n[t>>5]|=128<>>9)<<4)]=t);for(var c=1732584193,f=-271733879,i=-1732584194,a=271733878,h=0;h>5]>>>e%32)&255);return t}function h(n){var t=[];for(t[(n.length>>2)-1]=void 0,e=0;e>5]|=(255&n.charCodeAt(e/8))<>>4)&15)+r.charAt(15&t));return e}function r(n){return unescape(encodeURIComponent(n))}function o(n){return a(i(h((t=r(n))),8*t.length));var t}function u(n,t){return(function(n,t){var r,e,o=h(n),u=[],c=[];for(u[15]=c[15]=void 0,16 { + format = { + font: 'light', + size: 14, + color: this.widgetColor, + opacity: 1, + minimumScaleFactor: 1, + ...format, + }; const textItem = container.addText(string); const textFont = format.font; const textSize = format.size; @@ -1159,6 +2087,8 @@ class DmYY { textItem.font = this.provideFont(textFont, textSize); textItem.textColor = textColor; + textItem.textOpacity = format.opacity || 1; + textItem.minimumScaleFactor = format.minimumScaleFactor || 1; return textItem; }; } @@ -1178,9 +2108,9 @@ const Runing = async (Widget, default_args = '', isDebug = true, extra) => { const W = await M.render(); try { if (M.settings.refreshAfterDate) { - W.refreshAfterDate = new Date( - new Date() + 1000 * 60 * parseInt(M.settings.refreshAfterDate), - ); + const refreshTime = parseInt(M.settings.refreshAfterDate) * 1000 * 60; + const timeStr = new Date().getTime() + refreshTime; + W.refreshAfterDate = new Date(timeStr); } } catch (e) { console.log(e); @@ -1197,50 +2127,112 @@ const Runing = async (Widget, default_args = '', isDebug = true, extra) => { M[key] = extra[key]; }); } - if (__size) M.init(__size); + if (__size) M._init(__size); if (!act || !M['_actions']) { // 弹出选择菜单 const actions = M['_actions']; - const table = new UITable(); const onClick = async (item) => { M.widgetFamily = item.val; + try { + M._init(item.val); + } catch (error) { + console.log('初始化异常:' + error); + } w = await M.render(); const fnc = item.val .toLowerCase() .replace(/( |^)[a-z]/g, (L) => L.toUpperCase()); - w && (await w[`present${fnc}`]()); + if (w) return w[`present${fnc}`](); }; - const preview = [ - { + + const preview = [], + lockView = []; + if (M.renderSmall) { + preview.push({ + url: `https://raw.githubusercontent.com/dompling/Scriptable/master/images/small.png`, title: '小尺寸', val: 'small', + name: 'small', + dismissOnSelect: true, onClick, - }, - { + }); + } + + if (M.renderMedium) { + preview.push({ + url: `https://raw.githubusercontent.com/dompling/Scriptable/master/images/medium.png`, title: '中尺寸', val: 'medium', + name: 'medium', + dismissOnSelect: true, onClick, - }, - { + }); + } + + if (M.renderLarge) { + preview.push({ + url: `https://raw.githubusercontent.com/dompling/Scriptable/master/images/large.png`, title: '大尺寸', val: 'large', + name: 'large', + dismissOnSelect: true, + onClick, + }); + } + + if (M.renderAccessoryInline) { + lockView.push({ + icon: { + color: '#4676EE', + name: 'list.triangle', + }, + title: '锁屏列表', + val: 'accessoryInline', + name: 'accessoryInline', + dismissOnSelect: true, + onClick, + }); + } + + if (M.renderAccessoryRectangular) { + lockView.push({ + icon: { + color: '#4676EE', + name: 'arrow.rectanglepath', + }, + title: '锁屏 2x', + val: 'accessoryRectangular', + name: 'accessoryRectangular', + dismissOnSelect: true, + onClick, + }); + } + + if (M.renderAccessoryCircular) { + lockView.push({ + icon: { + color: '#4676EE', + name: 'circle.circle', + }, + title: '锁屏 1x', + val: 'accessoryCircular', + name: 'accessoryCircular', + dismissOnSelect: true, onClick, - }, - ]; - await M.preferences(table, preview, '预览组件'); - const extra = []; - for (let _ in actions) { - extra.push({ - title: _, - onClick: actions[_], }); } - await M.preferences(table, extra, '配置组件'); - await table.present(); + + const menuConfig = [ + ...(preview ? [{ title: '预览组件', menu: preview }] : []), + ...(lockView.length ? [{ title: '锁屏组件', menu: lockView }] : []), + ...M['_menuActions'], + ]; + + if (actions.length) menuConfig.push({ title: '组件配置', menu: actions }); + + await M.renderAppView(menuConfig, true); } } }; - +// await new DmYY().setWidgetConfig(); module.exports = { DmYY, Runing }; - -//version:1.0.4 diff --git a/Scripts/Ftms.js b/Scripts/Ftms.js new file mode 100644 index 0000000..59acc51 --- /dev/null +++ b/Scripts/Ftms.js @@ -0,0 +1,146 @@ +class Ftms { + constructor(carWidget) { + this.$ = carWidget; + } + + name = '一汽丰田'; + en = 'ftms'; + logo = 'https://www.toyota.com.cn/favicon.ico'; + + baseOpt = { + headers: { + Connection: `keep-alive`, + Host: `appiov.ftms.com.cn`, + 'Content-Type': `application/json`, + }, + body: ``, + }; + + init = async () => { + if (this.$.settings.dataSource) { + this.$.serveInfo = this.$.settings.serveInfo; + this.$.dataSource = this.$.settings.dataSource; + } else { + await this.cacheData(); + } + this.cacheData(); + }; + + cacheData = async () => { + try { + await this.getOilPrice(); + await this.getBmuServeHicleInfo(); + await this.getRemoteInfoDetail(); + } catch (e) { + console.log(e); + } + }; + + getBaseOptions(api) { + const baseURL = `https://appiov.ftms.com.cn`; + console.log({ url: `${baseURL}/${api}`, ...this.baseOpt }); + return { url: `${baseURL}/${api}`, ...this.baseOpt }; + } + + getRemoteInfoDetail = async () => { + const options = this.getBaseOptions( + 'ftms-iov-app-gbook/api/gbook/getRemoteInfoDetail' + ); + const response = await this.$.$request.post(options); + if (response.msg === 'success') { + this.$.dataSource.remoteInfo = response.result; + const safeData = + response.result.list.filter((item) => item.security !== 'safe') || []; + if (safeData.length > 0) { + this.$.dataSource.safeText = `${safeData[0].typeName}:${safeData[0].dataName}`; + } else { + this.$.dataSource.safeText = ``; + } + const dataTime = this.$.dataSource.remoteInfo.datatime.split('-'); + this.$.dataSource.remoteInfo.datatime = `${dataTime[1] || ''}-${ + dataTime[2] || '' + }`; + } else { + this.$.notify(this.name, response.msg); + } + await this.getDrivingMonitorInfo(); + }; + + getDrivingMonitorInfo = async () => { + const options = this.getBaseOptions( + 'ftms-iov-app-gbook/api/gbook/getDrivingMonitorInfo' + ); + const response = await this.$.$request.post(options); + console.log(response); + if (response.msg === 'success') { + this.$.dataSource.monitorInfo = response.result; + } + this.$.dataSource.monitorInfo.oilWasteText = `油耗:${this.$.dataSource.monitorInfo.oilWaste}L/100km`; + this.$.settings.dataSource = this.$.dataSource; + this.$.saveSettings(false); + }; + + getBmuServeHicleInfo = async () => { + let headers = await this.$.getCache('@ftms.headers'); + headers = JSON.parse(headers || '{}'); + this.baseOpt.headers = { + token: headers.token, + 'User-Agent': headers['User-Agent'], + ...this.baseOpt.headers, + }; + const options = { + url: `https://superapp.ftms.com.cn/superapp/users/wt/getbmuservehicleinfo?scriptable=1`, + headers, + }; + if (!this.$.settings.serveInfo) { + const response = await this.$.$request.post(options); + console.log(response); + if (response.code === '200') { + this.$.settings.serveInfo = response.data; + this.$.serveInfo = response.data; + this.$.saveSettings(false); + } else { + this.$.notify(this.name, response.msg); + } + } else { + this.$.serveInfo = this.$.settings.serveInfo || {}; + } + + this.baseOpt.headers.userId = this.$.serveInfo.userId; + this.baseOpt.headers['USER-ID'] = this.$.serveInfo.userId; + this.baseOpt.body = JSON.stringify({ vin: this.$.serveInfo.vin }); + this.baseOpt.headers.Authorization = `Bearer ${this.baseOpt.headers.token}`; + this.baseOpt.headers.accessToken = this.baseOpt.headers.token; + this.baseOpt.headers['ACCESS-TOKEN'] = this.baseOpt.headers.token; + }; + + getOilPrice = async () => { + const location = await Location.current(); + const locationText = await Location.reverseGeocode( + location.latitude, + location.longitude + ); + const { administrativeArea = '' } = locationText[0] || {}; + + const oilNumber = `${this.$.settings.oilNumber || '92'}`; + + const filter = `(CITYNAME="${administrativeArea.replace('省', '')}")`; + const time = Date.now(); + const url = `https://datacenter-web.eastmoney.com/api/data/v1/get?reportName=RPTA_WEB_YJ_JH&columns=ALL&filter=${encodeURIComponent( + filter + )}&sortColumns=DIM_DATE&sortTypes=-1&pageNumber=1&pageSize=1&source=WEB&_=${time}`; + + const options = { url }; + const response = await this.$.$request.post(options); + console.log(response); + if (response.result) { + this.$.dataSource.oilPrice = response.result.data[0]; + this.$.dataSource.oilZDE = response.result.data[0][`ZDE${oilNumber}`]; + this.$.dataSource.oilPriceText = `油价:${ + response.result.data[0][`V${oilNumber}`] + }`; + } + }; +} + +module.exports = Ftms; diff --git a/Scripts/Health.js b/Scripts/Health.js index 894e4d5..582bbbe 100644 --- a/Scripts/Health.js +++ b/Scripts/Health.js @@ -9,367 +9,396 @@ // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule +const { DmYY, Runing } = require('./DmYY') // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "健康行走步数"; - this.en = "healthCenter"; - this.maxMonthDist = parseInt(this.settings.maxMonthDist) || 5; // 柱状图比例高度,值越大,柱状范围越广 - this.Run(); - } - - widgetFamily = "medium"; - maxYearDist = 1500; - - color1 = Color.orange(); - lineColor = new Color("#48484b"); - useBoxJS = false; - - running = {}; - stepsCount = 0; - stepsToday = 0; - - init = async () => { - try { - await this.getData(); - } catch (e) { - console.log(e); - } - }; - - numberFormat(value) { - try { - const param = {}; - let k = 10000; - const size = ["", '万', "亿", "万亿"]; - let i; - if (value < k) { - param.value = value; - param.unit = ""; - } else { - i = Math.floor(Math.log(value) / Math.log(k)); - param.value = ((value / Math.pow(k, i))).toFixed(2); - param.unit = size[i]; - } - return param; - } catch (e) { - console.log(e); - } - } - - - getData = async () => { - try { - const fileICloud = FileManager.iCloud(); - const dir = fileICloud.documentsDirectory(); - const path = fileICloud.joinPath(dir, "health.txt"); - const response = fileICloud.readString(path); - let data = JSON.parse(response); - const dateToday = new Date(); - const year = dateToday.getFullYear(); - let month = dateToday.getMonth() + 1; - let day = dateToday.getDate(); - month = month >= 10 ? month : `0${month}`; - day = day >= 10 ? day : `0${day}`; - const today = `${year}-${month}-${day}`; - - data.forEach((item) => { - if (item.health_type === "Walking + Running Distance") { - item.samples.forEach((run, index) => { - if (item.samples.length - 1 === index) return; - const date = run.date; - if (!this.running[date]) this.running[date] = 0; - this.running[date] += parseFloat(run.value); - }); - } - if (item.health_type === "Steps") { - item.samples.forEach((step) => { - if (step.date === today) this.stepsToday = step.value; - this.stepsCount += parseInt(step.value); - }); - } - }); - Object.keys(this.running).forEach((key) => { - this.running[key] = Math.floor(this.running[key] * 100) / 100; - }); - } catch (e) { - this.notify( - this.name, - "健康数据读取失败,请点击使用健康数据快捷指令更新步数", - "https://www.icloud.com/shortcuts/beb65db5ea0a474abe7ff080410b9ddf", - ); - return false; - } - }; - - /*------------------------------------------------------------------------------ + constructor(arg) { + super(arg) + this.name = '健康行走步数' + this.en = 'healthCenter' + this.maxMonthDist = parseInt(this.settings.maxMonthDist) || 5 // 柱状图比例高度,值越大,柱状范围越广 + this.Run() + } + + widgetFamily = 'medium' + maxYearDist = 1500 + + color1 = Color.orange() + lineColor = new Color('#48484b') + useBoxJS = false + + running = {} + stepsCount = 0 + stepsToday = 0 + + init = async () => { + try { + await this.getData() + } catch (e) { + console.log(e) + } + } + + numberFormat(value) { + try { + const param = {} + let k = 10000 + const size = ['', '万', '亿', '万亿'] + let i + if (value < k) { + param.value = value + param.unit = '' + } else { + i = Math.floor(Math.log(value) / Math.log(k)) + param.value = (value / Math.pow(k, i)).toFixed(2) + param.unit = size[i] + } + return param + } catch (e) { + console.log(e) + } + } + + getData = async () => { + try { + const fileICloud = FileManager.iCloud() + const dir = fileICloud.documentsDirectory() + const path = fileICloud.joinPath(dir, 'health.txt') + const response = fileICloud.readString(path) + let data = JSON.parse(response) + const dateToday = new Date() + const year = dateToday.getFullYear() + let month = dateToday.getMonth() + 1 + let day = dateToday.getDate() + month = month >= 10 ? month : `0${month}` + day = day >= 10 ? day : `0${day}` + const today = `${year}-${month}-${day}` + + data.forEach((item) => { + if (item.health_type === 'Walking + Running Distance') { + item.samples.forEach((run, index) => { + if (item.samples.length - 1 === index) return + const date = run.date + if (!this.running[date]) this.running[date] = 0 + this.running[date] += parseFloat(run.value) + }) + } + if (item.health_type === 'Steps') { + item.samples.forEach((step) => { + if (step.date === today) this.stepsToday = step.value + this.stepsCount += parseInt(step.value) + }) + } + }) + Object.keys(this.running).forEach((key) => { + this.running[key] = Math.floor(this.running[key] * 100) / 100 + }) + } catch (e) { + this.notify( + this.name, + '健康数据读取失败,请点击使用健康数据快捷指令更新步数', + 'https://www.icloud.com/shortcuts/beb65db5ea0a474abe7ff080410b9ddf' + ) + return false + } + } + + /*------------------------------------------------------------------------------ 50 km Linien ------------------------------------------------------------------------------*/ - createLines(stack) { - let canvas, path; - // 50km Linien - canvas = new DrawContext(); - canvas.size = new Size(292, 82); - canvas.opaque = false; - canvas.respectScreenScale = true; - canvas.setFillColor(this.lineColor); - path = new Path(); - path.addRect(new Rect(0, 0, 292, 1)); - canvas.addPath(path); - canvas.fillPath(); - path = new Path(); - path.addRect(new Rect(0, 15, 292, 1)); - canvas.addPath(path); - canvas.fillPath(); - path = new Path(); - path.addRect(new Rect(0, 30, 292, 1)); - canvas.addPath(path); - canvas.fillPath(); - path = new Path(); - path.addRect(new Rect(0, 45, 292, 1)); - canvas.addPath(path); - canvas.fillPath(); - stack.backgroundImage = canvas.getImage(); - } - - async buildWidget(widget) { - // // Stacks definieren - let stackYear = widget.addStack(); - widget.addSpacer(); - let stackMonth = widget.addStack(); - // Stacks für Symbol und Jahresauswertung aufbereiten - let stackYear1 = stackYear.addStack(); - stackYear.addSpacer(10); - let stackYear2 = stackYear.addStack(); - let sym = SFSymbol.named("figure.walk"); - let img = stackYear1.addImage(sym.image); - img.tintColor = this.color1; - img.imageSize = new Size(25, 25); - stackYear2.layoutVertically(); - let stackYearCurr = stackYear2.addStack(); - let stackThemItem = stackYear2.addStack(); - let stackToday = stackYear2.addStack(); - - let data = 0; - const runningData = Object.keys(this.running); - if (runningData.length > 12) runningData.splice(0, runningData.length - 12); - runningData.forEach((date) => { - const [_, month, day] = date.split("-"); - const stackDay = stackMonth.addStack(); - const value = this.running[date]; - this.createProgressMonth(stackDay, `${month}.${day}`, value); - stackMonth.addSpacer(2); - data += value; - }); - this.createProgressYear(stackYearCurr, "运动", data, this.color1); - - const count = (18 * this.stepsCount) / (20000 * this.maxMonthDist / 4); - this.createProgressSteps(stackThemItem, "步数", this.stepsCount, this.color1, count); - const today = (18 * this.stepsToday) / (2000 * this.maxMonthDist / 4); - this.createProgressSteps(stackToday, "今日", this.stepsToday, this.color1, today); - - // 50km Linie - this.createLines(stackMonth); - return widget; - } - - createProgressYear(stack, year, dist, color) { - let stackDesc, stackPBar, stackDist, canvas, path, txt, img; - - // Initialisierung - stack.centerAlignContent(); - - // Stacks definieren - stackDesc = stack.addStack(); - stackPBar = stack.addStack(); - stackDist = stack.addStack(); - - // Beschreibung - stackDesc.size = new Size(30, 0); - txt = stackDesc.addText(year); - txt.font = Font.systemFont(7); - txt.textColor = this.widgetColor; - stackDesc.addSpacer(); - - // Progress-Bar - canvas = new DrawContext(); - canvas.size = new Size(180, 7); - canvas.opaque = false; - canvas.respectScreenScale = true; - canvas.setFillColor(new Color("#48484b")); - path = new Path(); - path.addRoundedRect(new Rect(0, 0, 180, 5), 3, 2); - canvas.addPath(path); - canvas.fillPath(); - canvas.setFillColor(color); - path = new Path(); - path.addRoundedRect(new Rect(0, 0, (180 * dist) / (200 * this.maxMonthDist / 4), 5), 3, 2); - canvas.addPath(path); - canvas.fillPath(); - img = stackPBar.addImage(canvas.getImage()); - img.imageSize = new Size(180, 7); - - // Distanz - stackDist.addSpacer(10); - txt = stackDist.addText(Math.round(dist).toString() + " km"); - txt.font = Font.systemFont(7); - txt.textColor = this.widgetColor; - } - - createProgressSteps(stack, year, dist, color, rectScale) { - let stackDesc, stackPBar, stackDist, canvas, path, txt, img; - - // Initialisierung - stack.centerAlignContent(); - - // Stacks definieren - stackDesc = stack.addStack(); - stackPBar = stack.addStack(); - stackDist = stack.addStack(); - - // Beschreibung - stackDesc.size = new Size(30, 0); - txt = stackDesc.addText(year); - txt.font = Font.systemFont(7); - txt.textColor = this.widgetColor; - stackDesc.addSpacer(); - - // Progress-Bar - canvas = new DrawContext(); - canvas.size = new Size(180, 7); - canvas.opaque = false; - canvas.respectScreenScale = true; - canvas.setFillColor(new Color("#48484b")); - path = new Path(); - path.addRoundedRect(new Rect(0, 0, 180, 5), 3, 2); - canvas.addPath(path); - canvas.fillPath(); - canvas.setFillColor(color); - path = new Path(); - const numberText = this.numberFormat(dist); - - path.addRoundedRect(new Rect(0, 0, rectScale, 5), 3, 2); - canvas.addPath(path); - canvas.fillPath(); - img = stackPBar.addImage(canvas.getImage()); - img.imageSize = new Size(180, 7); - - // Distanz - stackDist.addSpacer(10); - - txt = stackDist.addText(numberText.value + ` ${numberText.unit}步`); - txt.font = Font.systemFont(7); - txt.textColor = this.widgetColor; - } - - createTemplateItem(stack, desc) { - // Stacks - const txt = stack.addText(desc); - txt.font = Font.systemFont(7); - txt.textColor = this.widgetColor; - } - - /*------------------------------------------------------------------------------ + createLines(stack) { + let canvas, path + // 50km Linien + canvas = new DrawContext() + canvas.size = new Size(292, 82) + canvas.opaque = false + canvas.respectScreenScale = true + canvas.setFillColor(this.lineColor) + path = new Path() + path.addRect(new Rect(0, 0, 292, 1)) + canvas.addPath(path) + canvas.fillPath() + path = new Path() + path.addRect(new Rect(0, 15, 292, 1)) + canvas.addPath(path) + canvas.fillPath() + path = new Path() + path.addRect(new Rect(0, 30, 292, 1)) + canvas.addPath(path) + canvas.fillPath() + path = new Path() + path.addRect(new Rect(0, 45, 292, 1)) + canvas.addPath(path) + canvas.fillPath() + stack.backgroundImage = canvas.getImage() + } + + async buildWidget(widget) { + // // Stacks definieren + let stackYear = widget.addStack() + widget.addSpacer() + let stackMonth = widget.addStack() + // Stacks für Symbol und Jahresauswertung aufbereiten + let stackYear1 = stackYear.addStack() + stackYear.addSpacer(10) + let stackYear2 = stackYear.addStack() + let sym = SFSymbol.named('figure.walk') + let img = stackYear1.addImage(sym.image) + img.tintColor = this.color1 + img.imageSize = new Size(25, 25) + stackYear2.layoutVertically() + let stackYearCurr = stackYear2.addStack() + let stackThemItem = stackYear2.addStack() + let stackToday = stackYear2.addStack() + + let data = 0 + const runningData = Object.keys(this.running) + if (runningData.length > 12) runningData.splice(0, runningData.length - 12) + runningData.forEach((date) => { + const [_, month, day] = date.split('-') + const stackDay = stackMonth.addStack() + const value = this.running[date] + this.createProgressMonth(stackDay, `${month}.${day}`, value) + stackMonth.addSpacer(2) + data += value + }) + this.createProgressYear(stackYearCurr, '运动', data, this.color1) + + const count = (18 * this.stepsCount) / ((20000 * this.maxMonthDist) / 4) + this.createProgressSteps( + stackThemItem, + '步数', + this.stepsCount, + this.color1, + count + ) + const today = (18 * this.stepsToday) / ((2000 * this.maxMonthDist) / 4) + this.createProgressSteps( + stackToday, + '今日', + this.stepsToday, + this.color1, + today + ) + + // 50km Linie + this.createLines(stackMonth) + return widget + } + + createProgressYear(stack, year, dist, color) { + let stackDesc, stackPBar, stackDist, canvas, path, txt, img + + // Initialisierung + stack.centerAlignContent() + + // Stacks definieren + stackDesc = stack.addStack() + stackPBar = stack.addStack() + stackDist = stack.addStack() + + // Beschreibung + stackDesc.size = new Size(30, 0) + txt = stackDesc.addText(year) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + stackDesc.addSpacer() + + // Progress-Bar + canvas = new DrawContext() + canvas.size = new Size(180, 7) + canvas.opaque = false + canvas.respectScreenScale = true + canvas.setFillColor(new Color('#48484b')) + path = new Path() + path.addRoundedRect(new Rect(0, 0, 180, 5), 3, 2) + canvas.addPath(path) + canvas.fillPath() + canvas.setFillColor(color) + path = new Path() + path.addRoundedRect( + new Rect(0, 0, (180 * dist) / ((200 * this.maxMonthDist) / 4), 5), + 3, + 2 + ) + canvas.addPath(path) + canvas.fillPath() + img = stackPBar.addImage(canvas.getImage()) + img.imageSize = new Size(180, 7) + + // Distanz + stackDist.addSpacer(10) + txt = stackDist.addText(Math.round(dist).toString() + ' km') + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + } + + createProgressSteps(stack, year, dist, color, rectScale) { + let stackDesc, stackPBar, stackDist, canvas, path, txt, img + + // Initialisierung + stack.centerAlignContent() + + // Stacks definieren + stackDesc = stack.addStack() + stackPBar = stack.addStack() + stackDist = stack.addStack() + + // Beschreibung + stackDesc.size = new Size(30, 0) + txt = stackDesc.addText(year) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + stackDesc.addSpacer() + + // Progress-Bar + canvas = new DrawContext() + canvas.size = new Size(180, 7) + canvas.opaque = false + canvas.respectScreenScale = true + canvas.setFillColor(new Color('#48484b')) + path = new Path() + path.addRoundedRect(new Rect(0, 0, 180, 5), 3, 2) + canvas.addPath(path) + canvas.fillPath() + canvas.setFillColor(color) + path = new Path() + const numberText = this.numberFormat(dist) + + path.addRoundedRect(new Rect(0, 0, rectScale, 5), 3, 2) + canvas.addPath(path) + canvas.fillPath() + img = stackPBar.addImage(canvas.getImage()) + img.imageSize = new Size(180, 7) + + // Distanz + stackDist.addSpacer(10) + + txt = stackDist.addText(numberText.value + ` ${numberText.unit}步`) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + } + + createTemplateItem(stack, desc) { + // Stacks + const txt = stack.addText(desc) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + } + + /*------------------------------------------------------------------------------ Balkenanzeige für Monatsauswertung aufbereiten ------------------------------------------------------------------------------*/ - createProgressMonth(stack, month, dist3) { - let stackDist, stackPBar, stackDesc, canvas, path, s, img, txt; - - // Stacks definieren - stack.layoutVertically(); - stackPBar = stack.addStack(); - stack.addSpacer(5); - stackDesc = stack.addStack(); - stackDist = stack.addStack(); - - // Progress-Bar - canvas = new DrawContext(); - canvas.size = new Size(17, 60); - canvas.opaque = false; - canvas.respectScreenScale = true; - - canvas.setFillColor(this.color1); - path = new Path(); - s = (50 * dist3) / this.maxMonthDist; - path.addRect(new Rect(6, 60 - s, 8, s)); - canvas.addPath(path); - canvas.fillPath(); - img = stackPBar.addImage(canvas.getImage()); - img.imageSize = new Size(17, 60); - - // Monat - stackDesc.size = new Size(23, 10); - txt = stackDesc.addText(month); - txt.font = Font.systemFont(7); - txt.textColor = this.widgetColor; - txt.centerAlignText(); - - // Distanz aktuelle Jahr - stackDist.size = new Size(20, 8); - txt = stackDist.addText(Math.round(dist3).toString()); - txt.font = Font.systemFont(6); - txt.textColor = this.widgetColor; - txt.centerAlignText(); - } - - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - await this.buildWidget(widget); - await widget.presentMedium(); - if (config.runsFromHomeScreen) return widget; - } - - Run = () => { - if (config.runsInApp) { - this.registerAction("捷径安装", async () => { - await this.notify(this.name, "点击安装捷径", "https://www.icloud.com/shortcuts/beb65db5ea0a474abe7ff080410b9ddf"); - }); - this.registerAction("柱状比例", async () => { - await this.setAlertInput("设置柱状比例", " 柱状图比例高度,值越大,柱状范围越广", { maxMonthDist: "比例默认值,5" }); - }); - this.registerAction("皮肤颜色", this.setWidgetSkin); - this.registerAction("刻度颜色", this.setWidgetScale); - this.registerAction("基础设置", this.setWidgetConfig); - } - const skinColor = !this.isNight ? this.settings.lightSkinColor : this.settings.darkSkinColor; - this.color1 = skinColor ? new Color(skinColor) : this.color1; - const scaleColor = !this.isNight ? this.settings.lightScaleColor : this.settings.darkScaleColor; - this.lineColor = scaleColor ? new Color(scaleColor) : this.lineColor; - }; - - setWidgetSkin = async () => { - await this.setLightAndDark("柱状颜色", false, "lightSkinColor", "darkSkinColor"); - }; - - setWidgetScale = async () => { - await this.setLightAndDark("刻度颜色", false, "lightScaleColor", "darkScaleColor"); - }; + createProgressMonth(stack, month, dist3) { + let stackDist, stackPBar, stackDesc, canvas, path, s, img, txt + + // Stacks definieren + stack.layoutVertically() + stackPBar = stack.addStack() + stack.addSpacer(5) + stackDesc = stack.addStack() + stackDist = stack.addStack() + + // Progress-Bar + canvas = new DrawContext() + canvas.size = new Size(17, 60) + canvas.opaque = false + canvas.respectScreenScale = true + + canvas.setFillColor(this.color1) + path = new Path() + s = (50 * dist3) / this.maxMonthDist + path.addRect(new Rect(6, 60 - s, 8, s)) + canvas.addPath(path) + canvas.fillPath() + img = stackPBar.addImage(canvas.getImage()) + img.imageSize = new Size(17, 60) + + // Monat + stackDesc.size = new Size(23, 10) + txt = stackDesc.addText(month) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + txt.centerAlignText() + + // Distanz aktuelle Jahr + stackDist.size = new Size(20, 8) + txt = stackDist.addText(Math.round(dist3).toString()) + txt.font = Font.systemFont(6) + txt.textColor = this.widgetColor + txt.centerAlignText() + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init() + const widget = new ListWidget() + await this.getWidgetBackgroundImage(widget) + await this.buildWidget(widget) + await widget.presentMedium() + if (config.runsFromHomeScreen) return widget + } + + Run = () => { + if (config.runsInApp) { + this.registerAction('捷径安装', async () => { + await this.notify( + this.name, + '点击安装捷径', + 'https://www.icloud.com/shortcuts/beb65db5ea0a474abe7ff080410b9ddf' + ) + }) + this.registerAction('柱状比例', async () => { + await this.setAlertInput( + '设置柱状比例', + ' 柱状图比例高度,值越大,柱状范围越广', + { maxMonthDist: '比例默认值,5' } + ) + }) + this.registerAction('皮肤颜色', this.setWidgetSkin) + this.registerAction('刻度颜色', this.setWidgetScale) + this.registerAction('基础设置', this.setWidgetConfig) + } + const skinColor = !this.isNight + ? this.settings.lightSkinColor + : this.settings.darkSkinColor + this.color1 = skinColor ? new Color(skinColor) : this.color1 + const scaleColor = !this.isNight + ? this.settings.lightScaleColor + : this.settings.darkScaleColor + this.lineColor = scaleColor ? new Color(scaleColor) : this.lineColor + } + + setWidgetSkin = async () => { + await this.setLightAndDark( + '柱状颜色', + false, + 'lightSkinColor', + 'darkSkinColor' + ) + } + + setWidgetScale = async () => { + await this.setLightAndDark( + '刻度颜色', + false, + 'lightScaleColor', + 'darkScaleColor' + ) + } } - -// @组件代码结束 -if (config.runsFromHomeScreen || config.runsInApp) { - Runing(Widget, "", false); +let params = args.shortcutParameter +if (params) { + const fileICloud = FileManager.iCloud() + const path = fileICloud.documentsDirectory() + fileICloud.writeString(path + '/health.txt', JSON.stringify(params)) + Script.complete() } else { - let params = args.shortcutParameter; - if (params) { - const fileICloud = FileManager.iCloud(); - const path = fileICloud.documentsDirectory(); - fileICloud.writeString(path + "/health.txt", JSON.stringify(params)); - } - (async () => { - const M = new Widget(); - await M.render(); - Script.complete(); - })(); + await Runing(Widget, '', false) } +// @组件代码结束 diff --git a/Scripts/HistoryToday.js b/Scripts/HistoryToday.js index 4ec1906..56a4cde 100644 --- a/Scripts/HistoryToday.js +++ b/Scripts/HistoryToday.js @@ -3,160 +3,155 @@ // icon-color: green; icon-glyph: calendar-minus; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "历史上的今天"; - this.en = "historyToday"; - this.logo = - "https://raw.githubusercontent.com/Orz-3/mini/master/Color/historyToday.png"; - config.runsInApp && - this.registerAction("基础设置", this.setWidgetConfig); - this.cacheName = this.md5(`dataSouce_${this.en}`); - } - - useBoxJS = false; - today = ""; - dataSource = []; - - init = async () => { - try { - const today = new Date(); - const month = today.getMonth() + 1; - const day = today.getDate(); - this.today = `${month}.${day}`; - await this.getHistoryList(); - } catch (e) { - console.log(e); - } - }; - - getHistoryList = async () => { - const url = `http://code.lssdjt.com/jsondata/history.${this.today}.js`; - const response = await this.$request.get(url); - try { - if (response.d.length > 0) { - const dataSource = response.d; - this.dataSource = dataSource.filter((item) => item.j[0]).map((item) => ({ ...item, j: item.j[0] })); - } - return false; - } catch (e) { - return false; - } - }; - - setListCell = async (cell, data) => { - let { j, t, f } = data; - const [year, desc] = t.split(' '); - const imgUri = "http://img.lssdjt.com"; - let body = cell.addStack(); - body.url = `https://www.lssdjt.com/d/${f}.htm`; - - const textView = body.addStack(); - textView.layoutVertically(); - - const descText = textView.addText(desc); - descText.font = Font.boldSystemFont(14); - descText.textColor = this.widgetColor; - descText.lineLimit = 1; - - textView.addSpacer(); - const subContent = textView.addText(year); - subContent.font = Font.lightSystemFont(10); - subContent.textColor = this.widgetColor; - subContent.lineLimit = 1; - - if (this.widgetFamily !== "small") { - body.addSpacer(); - const imageView = body.addStack(); - imageView.centerAlignContent(); - imageView.size = new Size(43, 43); - imageView.cornerRadius = 5; - imageView.borderWidth = 1; - imageView.borderColor = this.widgetColor; - const image = await this.$request.get(`${imgUri}/${j}`, "IMG"); - const imageItem = imageView.addImage(image); - imageItem.centerAlignImage(); - body.addSpacer(10); - } - return cell; - }; - - setWidget = async (body, size) => { - const container = body.addStack(); - container.layoutVertically(); - const dataSource = this.getRandomArrayElements(this.dataSource, size); - for (let index = 0; index < dataSource.length; index++) { - const data = dataSource[index]; - let listItem = container.addStack(); - await this.setListCell(listItem, data); - container.addSpacer(10); - } - body.addSpacer(); - return body; - }; - - renderSmall = async (w) => { - return await this.setWidget(w, 2); - }; - - renderLarge = async (w) => { - return await this.setWidget(w, 5); - }; - - renderMedium = async (w) => { - return await this.setWidget(w, 2); - }; - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - const header = widget.addStack(); - if (this.widgetFamily !== "small") { - await this.renderNotSmallHeader(header); - } else { - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - } - widget.addSpacer(10); - if (this.widgetFamily === "medium") { - return await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - return await this.renderLarge(widget); - } else { - return await this.renderSmall(widget); - } - } - - renderNotSmallHeader = async (header) => { - header.centerAlignContent(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - header.addSpacer(); - const headerMore = header.addStack(); - let [month, day] = this.today.split("."); - month = month >= 10 ? month : `0${month}`; - day = day >= 10 ? day : `0${day}`; - headerMore.url = `https://m.8684.cn/today_d${month}${day}`; - headerMore.setPadding(1, 10, 1, 10); - headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText(`更多`); - textItem.font = Font.boldSystemFont(12); - textItem.textColor = this.widgetColor; - textItem.lineLimit = 1; - textItem.rightAlignText(); - return header; - }; + constructor(arg) { + super(arg); + this.name = '历史上的今天'; + this.en = 'historyToday'; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/historyToday.png'; + config.runsInApp && this.registerAction('基础设置', this.setWidgetConfig); + this.cacheName = this.md5(`dataSouce_${this.en}`); + } + + useBoxJS = false; + today = ''; + dataSource = []; + + init = async () => { + try { + const today = new Date(); + const month = today.getMonth() + 1; + const day = today.getDate(); + this.today = `${month}.${day}`; + await this.getHistoryList(); + } catch (e) { + console.log(e); + } + }; + + getHistoryList = async () => { + const url = `http://api.sodion.net/api_v1/grap/todayinhistory`; + const response = await this.$request.get(url); + if (!response || !response.length) + console.log('接口数据异常,请稍后再试!'); + this.dataSource = response; + }; + + setListCell = async (cell, data) => { + let { year, title, href, img } = data; + let body = cell.addStack(); + body.url = href; + + const box = body.addStack(); + box.addSpacer(); + box.setPadding(10, 10, 10, 10); + box.backgroundColor = new Color('#000', 0.1); + box.cornerRadius = 20; + box.layoutVertically(); + + const boxTopStack = box.addStack(); + boxTopStack.addSpacer(); + const avatarStack = boxTopStack.addStack(); + avatarStack.size = new Size(50, 50); + avatarStack.cornerRadius = 25; + const image = await this.$request.get(img, 'IMG'); + const imageItem = avatarStack.addImage(image); + imageItem.centerAlignImage(); + boxTopStack.addSpacer(); + + box.addSpacer(); + const titleStack = box.addStack(); + titleStack.size = new Size(0, 30); + const descText = titleStack.addText(title); + descText.font = Font.boldSystemFont(8); + descText.textColor = this.widgetColor; + descText.lineLimit = 3; + + box.addSpacer(5); + const yearStack = box.addStack(); + yearStack.addSpacer(); + const yearText = yearStack.addText(year); + yearText.font = Font.boldSystemFont(10); + yearText.textColor = this.widgetColor; + yearStack.addSpacer(); + + return cell; + }; + + group(array, subNum) { + let index = 0; + let newArray = []; + while (index < array.length) { + newArray.push(array.slice(index, (index += subNum))); + } + return newArray; + } + + setWidget = async (body, size) => { + const container = body.addStack(); + container.setPadding(10, 10, 10, 10); + const data = this.getRandomArrayElements(this.dataSource, size); + if (size === 6) { + const source = this.group(data, 3); + container.layoutVertically(); + for (const item of source) { + const boxStack = container.addStack(); + container.addSpacer(); + for (let index = 0; index < item.length; index++) { + const data = item[index]; + let listItem = boxStack.addStack(); + await this.setListCell(listItem, data); + if (index !== item.length - 1) boxStack.addSpacer(); + } + } + } else { + const dataSource = data; + for (let index = 0; index < dataSource.length; index++) { + const data = dataSource[index]; + let listItem = container.addStack(); + await this.setListCell(listItem, data); + if (index !== dataSource.length - 1) container.addSpacer(); + } + } + return body; + }; + + renderSmall = async (w) => { + return await this.setWidget(w, 1); + }; + + renderLarge = async (w) => { + return await this.setWidget(w, 6); + }; + + renderMedium = async (w) => { + return await this.setWidget(w, 3); + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(0, 0, 0, 0); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/JD-all-one-v2.js b/Scripts/JD-all-one-v2.js new file mode 100644 index 0000000..19321c9 --- /dev/null +++ b/Scripts/JD-all-one-v2.js @@ -0,0 +1,1677 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: pink; icon-glyph: shopping-cart; +// Author: 脑瓜 +// 电报群: https://t.me/Scriptable_JS @anker1209 +// 采用了2Ya美女的京豆收支脚本及DmYY依赖 https://github.com/dompling/Scriptable/tree/master/Scripts +// version:2.2.5 +// update:2021/11/08 + +if (typeof require === 'undefined') require = importModule; +const {DmYY, Runing} = require('./DmYY'); + +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = '京东多合一'; + this.en = 'jd_in_one'; + this.run(module.filename, args); + } + fm = FileManager.local(); + CACHE_FOLDER = 'JD_in_one'; + cachePath = null; + + logo = 'https://pic.imgdb.cn/item/6187994b2ab3f51d919028cc.png'; + JDImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/43300bf7-61a2-4bd1-94a1-bf2faa2ed9e8.png'; + beanImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/7ea91cf8-6dea-477c-ae72-cb4d3f646c34.png'; + plusFG = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/cd0d2b80-0857-4202-8d12-af4eb7d241d6.png'; + plusBG = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/24fc5a14-edea-4b1b-8e30-bdcc1a27a037.png'; + baitiaoImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/30c40f5b-7428-46c3-a2c0-d81b2b95ec41.png'; + plusIcon = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/06f78540-a5a4-462e-b8c5-98cb8059efc1.png'; + walletImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/cd89ceec-7895-41ee-a1a3-3d3e7223035f.png'; + jingtieImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/16a7038e-6082-4ad8-b17f-fdd08266fb22.png'; + gangbengImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/9704e332-9e7f-47e8-b09a-1f1991d4aa84.png'; + userImage = 'https://img11.360buyimg.com/jdphoto/s120x120_jfs/t21160/90/706848746/2813/d1060df5/5b163ef9N4a3d7aa6.png'; + nameImg = 'https://pic.imgdb.cn/item/6188bfb62ab3f51d91bca276.png'; + tagImg = 'https://pic.imgdb.cn/item/6188bfc72ab3f51d91bcbcb0.png'; + + // 请勿在此修改参数值 + + version = '2.2.5'; + basicSetting = { + scale: 1.00, + logo: 30, + userImage: 69, + userStack: 103, + division: 25, + interval: 10, + directory: 'Local', + customizeName: '', + customizeAvatar: '', + smallShowType: '京豆、钱包数据', + walletShowType: '红包', + }; + chartSetting = { + height: 130, + daySize: 9, + dayText: '', + textSize: 18, + textDayColor: '999999', + textNightColor: '999999', + lineColor: '#FA6859', + linePadding: 15, + barPadding: 5, + smallShowType: '双日视图', + showType: '双日视图', + countBean: '收入-支出', + colorful: '关闭', + }; + funcSetting = { + showBaitiao: '打开', + showPackage: '关闭', + showFruit: '打开', + logable: '关闭', + alwaysRefreshChart: '打开', + }; + package = { + number: 0, + title: '', + desc: '', + time: '', + status: '', + }; + baitiao = { + title: '', + number: 0, + desc: '', + }; + redPackage = { + title: '红包', + number: 0, + desc: '今日无过期', + }; + extra = { + jingtie: 0, + gangbeng: 0, + }; + bean = { + todayIncome: 0, + todayExpense: 0, + ydayIncome: 0, + ydayExpense: 0, + }; + + nickName = '未知用户'; + jValue = '0'; + isPlus = false; + + cookie = ''; + userName = ''; + CookiesData = []; + cacheChart = false; + beanCount = 0; + maxDays = 6; + rangeTimer = {}; + timerKeys = []; + fruitState = "😢"; + + doubleDate = this.getDay(1); + doubleDay = Object.keys(this.doubleDate); + yestoday = this.doubleDay[0]; + today = this.doubleDay[1]; + CACHES = []; + + lineChart(labels = [], datas = [], chartTextSize, topPadding) { + let chartTextColor = Color.dynamic(new Color(this.chartSetting.textDayColor),new Color(this.chartSetting.textNightColor),); + let lineColor = this.chartSetting.lineColor.split(','); + const chartStr = ` + { + type: 'bar', + data: { + labels: ${JSON.stringify(labels)}, + datasets: [ + { + type: 'line', + backgroundColor: '#FFFFFF', + borderColor: getGradientFillHelper('horizontal', ${JSON.stringify(lineColor)}), + borderWidth: ${this.isSmall(true) ? 4 : 3}, + pointRadius: ${this.isSmall(true) ? 8 : 6}, + fill: false, + showLine: true, + data: ${JSON.stringify(datas)}, + }, + ], + }, + options: { + plugins: { + datalabels: { + display: true, + align: 'top', + color: '#${chartTextColor.hex}', + font: { + family: 'ArialMT', + size: ${chartTextSize} + } + }, + }, + layout: { + padding: { + left: -20, + right: 0, + top: ${topPadding}, + bottom: 0 + } + }, + responsive: true, + maintainAspectRatio: true, + legend: { + display: false, + }, + scales: { + xAxes: [ + { + gridLines: { + display: false, + }, + ticks: { + display: false, + }, + }, + ], + yAxes: [ + { + ticks: { + display: false, + beginAtZero: false, + }, + gridLines: { + display: false, + }, + }, + ], + }, + }, + }`; + return chartStr; + } + + barChart(labels = [], datas = [], chartTextSize, topPadding, showType) { + let chartTextColor = Color.dynamic(new Color(this.chartSetting.textDayColor),new Color(this.chartSetting.textNightColor),); + let backgroundColor = []; + if (this.chartSetting.colorful === '打开') backgroundColor = JSON.stringify(this.colorfulBar()) + else backgroundColor = `getGradientFillHelper('vertical', ${JSON.stringify(this.chartColors())})` + const chartStr = ` + { + type: 'bar', + data: { + labels: ${JSON.stringify(labels)}, + datasets: [ + { + type: '${showType}', + borderWidth: 0, + pointRadius: 0, + barPercentage: 0.5, + backgroundColor: ${backgroundColor}, + borderColor: false, + data: ${JSON.stringify(datas)}, + }, + ], + }, + options: { + plugins: { + datalabels: { + display: true, + align: 'top', + offset: -4, + anchor:'end', + color: '#${chartTextColor.hex}', + font: { + family: 'ArialMT', + size: ${chartTextSize} + } + }, + }, + layout: { + padding: { + left: -20, + right: 0, + top: ${topPadding}, + bottom: 0 + } + }, + responsive: true, + maintainAspectRatio: true, + legend: { + display: false, + }, + title: { + display: false, + }, + scales: { + xAxes: [ + { + gridLines: { + offsetGridLines: true, + display: false, + }, + ticks: { + display: false, + }, + }, + ], + yAxes: [ + { + ticks: { + display: false, + beginAtZero: true, + }, + gridLines: { + offsetGridLines: true, + display: false, + }, + }, + ], + }, + }, + }`; + return chartStr; + } + + chartColors () { + let colorArr = [['#FFF000', '#E62490'], ['#FDEB71', '#F8D800'], ['#ABDCFF', '#0396FF'], ['#FEB692', '#EA5455'], ['#FEB692', '#EA5455'], ['#CE9FFC', '#7367F0'], ['#90F7EC', '#32CCBC'], ['#FFF6B7', '#F6416C'], ['#E2B0FF', '#9F44D3'], ['#F97794', '#F072B6'], ['#FCCF31', '#F55555'], ['#5EFCE8', '#736EFE'], ['#FAD7A1', '#E96D71'], ['#FFFF1C', '#00C3FF'], ['#FEC163', '#DE4313'], ['#F6CEEC', '#D939CD'], ['#FDD819', '#E80505'], ['#FFF3B0', '#CA26FF'], ['#2AFADF', '#4C83FF'], ['#EECDA3', '#EF629F'], ['#C2E59C', '#64B3F4'], ['#FFF886', '#F072B6'], ['#F5CBFF', '#C346C2'], ['#FFF720', '#3CD500'], ['#EE9AE5', '#5961F9'], ['#FFC371', '#FF5F6D'], ['#FFD3A5', '#FD6585'], ['#C2FFD8', '#465EFB'], ['#FFC600', '#FD6E6A'], ['#FFC600', '#FD6E6A'], ['#92FE9D', '#00C9FF'], ['#FFDDE1', '#EE9CA7'], ['#F0FF00', '#58CFFB'], ['#FFE985', '#FA742B'], ['#72EDF2', '#5151E5'], ['#F6D242', '#FF52E5'], ['#F9D423', '#FF4E50'], ['#3C8CE7', '#00EAFF'], ['#FCFF00', '#FFA8A8'], ['#FF96F9', '#C32BAC'], ['#D0E6A5', '#FFDD94'], ['#FFDD94', '#FA897B'], ['#FFCC4B', '#FF7D58'], ['#D0E6A5', '#86E3CE'], ['#F0D5B6', '#F16238'], ['#F8EC70', '#F9C708'], ['#C4E86B', '#00BCB4'], ['#F5CEC7', '#E79796'], ['#FFC446', '#FA0874'], ['#E1EE32', '#FFB547'], ['#FFD804', '#2ACCC8'], ['#E9A6D2', '#E9037B'], ['#F8EC70', '#49E2F6'], ['#A2F8CD', '#A2F852'], ['#49E2F6', '#A2F8CD'], ['#FDEFE2', '#FE214F'], ['#F8EC70', '#A2F8CD'], ['#F8EC70', '#49E2F6'], ['#D1FFB7', '#FFB7D1'], ['#B7FFE4', '#E4B7FF'], ['#FFB7D1', '#E4B7FF'], ['#D0E6A5', '#86E3CE'], ['#E8E965', '#64C5C7']]; + let chartColors = colorArr[Math.floor(Math.random() * colorArr.length)]; + //chartColors = ['#DB36A4', '#F7FF00']; // 固定京豆图表填充颜色 + return chartColors; + } + + colorfulBar () { + let colorArr = [['#1B9E77', '#D95F02', '#7570B3', '#E7298A', '#66A61E', '#E6AB02'], ['#F46277', '#FC8D59', '#FEE08B', '#E6F598', '#99D594', '#3288BD'], ['#A6CEE3', '#1F78B4', '#B2DF8A', '#33A02C', '#FB9A99', '#E31A1C'], ['#E41A1C', '#377EB8', '#4DAF4A', '#984EA3', '#FF7F00', '#9ED80E'], ['#F81B02', '#FC7715', '#AFBF41', '#50C49F', '#3B95C4', '#B560D4'], ['#FFC000', '#A5D028', '#08CC78', '#F24099', '#5AA6C0', '#F56617'], ['#F09415', '#C1B56B', '#4BAF73', '#5AA6C0', '#D17DF9', '#FA7E5C'], ['#0F6FC6', '#009DD9', '#0BD0D9', '#10CF9B', '#7CCA62', '#A5C249'], ['#9ACD4C', '#FAA93A', '#D35940', '#B258D3', '#63A0CC', '#8AC4A7'], ['#A7EA52', '#EFAB16', '#78AC35', '#35ACA2', '#4083CF', '#FF8021'], ['#9EC544', '#50BEA3', '#4A9CCC', '#9A66CA', '#C54F71', '#DE9C3C'], ['#41AEBD', '#97E9D5', '#A2CF49', '#608F3D', '#F4DE3A', '#FCB11C'], ['#2FA3EE', '#4BCAAD', '#86C157', '#D99C3F', '#CE6633', '#A35DD1'], ['#3399FF', '#69FFFF', '#CCFF33', '#3333FF', '#9933FF', '#FF33FF'], ['#FBC01E', '#EFE1A2', '#FA8716', '#F2575F', '#A5D848', '#A155F9'], ['#90C226', '#54A021', '#E6B91E', '#E76618', '#C42F1A', '#FA8716'], ['#0F6FC6', '#009DD9', '#0BD0D9', '#10CF9B', '#7CCA62', '#A5C249'], ['#FFB91D', '#F97817', '#6DE304', '#F98080', '#8F58F9', '#F789EA'], ['#C70F0C', '#DD6B0D', '#FAA700', '#93E50D', '#17C7BA', '#0A96E4'], ['#40BAD2', '#FAB900', '#90BB23', '#EE7008', '#1AB39F', '#D5393D'], ['#80B606', '#E29F1D', '#2397E2', '#35ACA2', '#5430BB', '#8D34E0'], ['#549E39', '#8AB833', '#C0CF3A', '#029676', '#4AB5C4', '#0989B1'], ['#99CB38', '#63A537', '#37A76F', '#44C1A3', '#4EB3CF', '#51C3F9'], ['#8C73D0', '#C2E8C4', '#C5A6E8', '#B45EC7', '#9FDAFB', '#95C5B0'], ['#1CADE4', '#2683C6', '#27CED7', '#42BA97', '#3E8853', '#62A39F'], ['#B31166', '#E33D6F', '#E45F3C', '#E9943A', '#9B6BF2', '#D53DD0'], ['#76C5EF', '#FEA022', '#FF6700', '#70A525', '#A5D848', '#20768C'], ['#A1D68B', '#5EC795', '#4DADCF', '#CDB756', '#E29C36', '#8EC0C1'], ['#418AB3', '#A6B727', '#F69200', '#80C34F', '#FEC306', '#DF5327'], ['#7FD13B', '#EA157A', '#FEB80A', '#00ADDC', '#738AC8', '#1AB39F'], ['#F0AD00', '#60B5CC', '#E66C7D', '#6BB76D', '#E88651', '#C64847'], ['#5B9BD5', '#ED7D31', '#A5D848', '#FFC000', '#4472C4', '#70AD47'], ['#4F81BD', '#C0504D', '#9BBB59', '#8064A2', '#4BACC6', '#F79646'], ['#F95F9A', '#AC66BB', '#DE6C36', '#F9B639', '#CF6DA4', '#FA8D3D'], ['#F2D908', '#9DE61E', '#0D8BE6', '#C61B1B', '#E26F08', '#8D35D1'], ['#A5B592', '#F3A447', '#E7BC29', '#D092A7', '#9C85C0', '#809EC2'], ['#30ACEC', '#80C34F', '#E29D3E', '#D64A3B', '#D64787', '#A666E1'], ['#A2C816', '#E07602', '#E4C402', '#7DC1EF', '#21449B', '#A2B170'], ['#FF7F01', '#F1B015', '#FBEC85', '#D2C2F1', '#DA5AF4', '#9D09D1'], ['#FDA023', '#A7EA52', '#5ECCF3', '#64A73B', '#EB5605', '#B9CA1A'], ['#00C6BB', '#6FEBA0', '#B6DF5E', '#EFB251', '#EF755F', '#ED515C'], ['#E32D91', '#C830CC', '#4EA6DC', '#4775E7', '#8971E1', '#D54773'], ['#1CADE4', '#2683C6', '#27CED7', '#42BA97', '#3E8853', '#62A39F'], ['#073779', '#8FD9FB', '#FFCC00', '#EB6615', '#C76402', '#B523B4'], ['#4E67C8', '#5ECCF3', '#A7EA52', '#5DCEAF', '#FF8021', '#F14124'], ['#3891A7', '#FEB80A', '#FC8389', '#84AA33', '#F9934E', '#4379EF'], ['#990000', '#FF6600', '#FFBA00', '#99CC00', '#528A02', '#9C007F'], ['#F7901E', '#FEC60B', '#9FE62F', '#4EA5D1', '#4282EA', '#854FED'], ['#E8BC4A', '#83C1C6', '#E78D35', '#909CE1', '#839C41', '#F9826E'], ['#86CE24', '#00A2E6', '#FAC810', '#AA69F7', '#D06B20', '#FF8021'], ['#DF2E28', '#FE801A', '#E9BF35', '#81BB42', '#32C7A9', '#4A9BDC'], ['#31B6FD', '#4584D3', '#5BD078', '#A5D028', '#F5C040', '#05E0DB'], ['#FFCA08', '#F8931D', '#CE8D3E', '#EC7016', '#E64823', '#9C6A6A'], ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948'], ['#4E79A7', '#A0CBE8', '#F28E2B', '#FFBE7D', '#59A14F', '#8CD17D'], ['#E03531', '#F0BD27', '#51B364', '#FF684C', '#FFDA66', '#8ACE7E'], ['#4E9F50', '#87D180', '#EF8A0C', '#FCC66D', '#3CA8BC', '#98D9E4'], ['#1F77B4', '#FF7F0E', '#2CA02C', '#D62728', '#9467BD', '#E377C2'], ['#32A251', '#ACD98D', '#FF7F0F', '#FFB977', '#3CB7CC', '#98D9E4'], ]; + let chartColors = colorArr[Math.floor(Math.random() * colorArr.length)]; + //chartColors = ['#C1B14A','#6FBC75','#39B6B3','#86A1CD','#D083AB','#DF786B']; // 固定京豆图表填充颜色 + return chartColors; + } + + isSmall (a = false) { + if (a) return config.widgetFamily == 'small' ? true : false; + else return config.widgetFamily == 'small' ? '_small' : ''; + } + + // #####################小组件################### + renderSmall = async (w) => { + const bodyStack = w.addStack(); + bodyStack.layoutVertically(); + if (this.basicSetting.smallShowType === '个人信息') { + await this.setUserShow(bodyStack); + } else { + await this.setHeaderShow(bodyStack); + bodyStack.addSpacer(); + switch (this.chartSetting.smallShowType) { + case '折线图表' : + await this.setChartShow(bodyStack, 1); + break; + case '柱状图表' : + await this.setChartShow(bodyStack, 2); + break; + case '曲线面积图': + await this.setChartShow(bodyStack, 3); + break; + default: + await this.setBeanShow(bodyStack, 22 * this.basicSetting.scale, 40 * this.basicSetting.scale); + } + bodyStack.addSpacer(5 * this.basicSetting.scale); + if (this.funcSetting.showBaitiao === '打开' && this.baitiao.number > 0) { + await this.setBaitiaoShow(bodyStack, true); + } else if (this.basicSetting.walletShowType === '红包') { + await this.setRedPackageShow(bodyStack, true); + } else { + await this.setCoinShow(bodyStack, true); + } + } + return w; + } + + // #####################中组件################### + renderMedium = async (w) => { + const bodyStack = w.addStack(); + await this.setUserShow(bodyStack); + bodyStack.addSpacer(this.basicSetting.division * this.basicSetting.scale); + const mainStack = bodyStack.addStack(); + mainStack.layoutVertically(); + await this.setHeaderShow(mainStack, this.JDImg); + mainStack.addSpacer(); + if (this.funcSetting.showPackage === '打开' && this.package.number > 0) { + await this.setPackageShow(mainStack); + mainStack.addSpacer(); + } else { + switch (this.chartSetting.showType) { + case '折线图表': + await this.setChartShow(mainStack, 1); + mainStack.addSpacer(5 * this.basicSetting.scale); + break; + case '柱状图表': + await this.setChartShow(mainStack, 2); + mainStack.addSpacer(5 * this.basicSetting.scale); + break; + case '曲线面积图': + await this.setChartShow(mainStack, 3); + mainStack.addSpacer(5 * this.basicSetting.scale); + break; + default: + await this.setBeanShow(mainStack, 30 * this.basicSetting.scale, 50 * this.basicSetting.scale); + mainStack.addSpacer(); + } + } + if (this.funcSetting.showBaitiao === '打开' && this.baitiao.number > 0) { + await this.setBaitiaoShow(mainStack); + } else if (this.basicSetting.walletShowType === '红包') { + await this.setRedPackageShow(mainStack); + } else { + await this.setCoinShow(mainStack); + } + return w; + } + + // #####################大组件################### + renderLarge = async (w) => { + const bodyStack = w.addStack(); + bodyStack.size = new Size(0, 150); + bodyStack.addSpacer(); + await this.setUserShow(bodyStack); + bodyStack.addSpacer(); + w.addSpacer(20); + const text = w.addText('\u6211\u600e\u4e48\u8fd9\u4e48\u597d\u770b'); + w.addSpacer(20); + text.font = Font.thinSystemFont(30); + text.centerAlignText(); + const emoji = w.addText('🤣🥰🤪'); + emoji.centerAlignText(); + w.addSpacer(); + return w; + } + + // #####################用户信息################### + async setUserShow(stack) { + const userStack = stack.addStack(); + userStack.size = new Size(this.basicSetting.userStack * this.basicSetting.scale, 0); + userStack.layoutVertically(); + // 头像 + const userImgStack = userStack.addStack(); + userImgStack.addSpacer(); + const imgStack = userImgStack.addStack(); + if (this.isPlus) { + imgStack.size = new Size(this.basicSetting.userImage * this.basicSetting.scale, this.basicSetting.userImage * this.basicSetting.scale * 1.039); + imgStack.backgroundImage = await this.getImageByUrl(this.plusBG, 'plusBGImage.png'); + } + const subStack = imgStack.addStack(); + subStack.url = 'openapp.jdmobile://'; + subStack.size = new Size(this.basicSetting.userImage * this.basicSetting.scale, this.basicSetting.userImage * this.basicSetting.scale); + subStack.cornerRadius = this.basicSetting.userImage / 2 * this.basicSetting.scale; + subStack.backgroundImage = await this.getImageByUrl(this.basicSetting.customizeAvatar || this.userImage, `userImage_${this.userName}.png`); + if (this.isPlus) { + const userImg = subStack.addImage(await this.getImageByUrl(this.plusFG, 'plusFGImage.png')); + } + userImgStack.addSpacer(); + userStack.addSpacer(); + // 物流提示 + const tipStack = userStack.addStack(); + tipStack.addSpacer(); + const signStack = tipStack.addStack(); + signStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) + signStack.backgroundColor = new Color('0dD6A0'); + signStack.cornerRadius = 14 * this.basicSetting.scale / 2; + signStack.centerAlignContent(); + let signIcon = SFSymbol.named('checkmark'); + const signItem = signStack.addImage(signIcon.image); + signItem.imageSize = new Size(8 * this.basicSetting.scale, 8 * this.basicSetting.scale); + signItem.tintColor = new Color('FFFFFF'); + if (this.package.number > 0) { + tipStack.addSpacer(3 * this.basicSetting.scale); + const packageStack = tipStack.addStack(); + packageStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) + packageStack.backgroundColor = new Color('FC8600'); + packageStack.cornerRadius = 14 * this.basicSetting.scale / 2; + packageStack.centerAlignContent(); + packageStack.setPadding(1 * this.basicSetting.scale, 2 * this.basicSetting.scale, 1 * this.basicSetting.scale, 2 * this.basicSetting.scale); + let packageNum = packageStack.addText(this.package.number.toString()); + packageNum.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + packageNum.textColor = new Color('FFFFFF'); + packageNum.minimumScaleFactor = 0.1; + } + if (this.funcSetting.showFruit === '打开') { + tipStack.addSpacer(3 * this.basicSetting.scale); + const fruitStack = tipStack.addStack(); + fruitStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) + fruitStack.backgroundColor = new Color('118AB2'); + fruitStack.cornerRadius = 14 * this.basicSetting.scale / 2; + fruitStack.centerAlignContent(); + fruitStack.setPadding(1 * this.basicSetting.scale, 2 * this.basicSetting.scale, 1 * this.basicSetting.scale, 2 * this.basicSetting.scale); + let fruitText = fruitStack.addText(this.fruitState); + fruitText.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + fruitText.textColor = new Color('FFFFFF'); + fruitText.minimumScaleFactor = 0.1; + } + tipStack.addSpacer(); + userStack.addSpacer(); + // 用户名 + const nameStack = userStack.addStack(); + nameStack.centerAlignContent(); + if (this.isPlus) { + const nameImg = nameStack.addImage(await this.getImageByUrl(this.plusIcon, 'plusIcon.png')); + nameImg.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); + } else { + const nameIcon = nameStack.addImage(await this.getImageByUrl(this.nameImg, 'nameImg.png')); + nameIcon.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); + } + nameStack.addSpacer(5 * this.basicSetting.scale); + const name = nameStack.addText(this.basicSetting.customizeName || this.nickName); + name.lineLimit = 1; + name.font = Font.regularSystemFont(14 * this.basicSetting.scale); + userStack.addSpacer(5 * this.basicSetting.scale); + // 京享值 + const valueStack = userStack.addStack(); + valueStack.centerAlignContent(); + const lableIcon = valueStack.addImage(await this.getImageByUrl(this.tagImg, 'tagImg.png')); + lableIcon.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); + valueStack.addSpacer(5 * this.basicSetting.scale); + const value = valueStack.addText(this.jValue.toString()); + value.font = Font.mediumSystemFont(14 * this.basicSetting.scale); + + valueStack.addSpacer(5 * this.basicSetting.scale); + const jStack = valueStack.addStack(); + jStack.backgroundColor = new Color('fa2d19'); // “京享”二字背景颜色 + jStack.cornerRadius = 5; + jStack.setPadding(1 * this.basicSetting.scale, 4 * this.basicSetting.scale, 1 * this.basicSetting.scale, 4 * this.basicSetting.scale); + const jLable = jStack.addText('京享'); + jLable.font = Font.systemFont(8 * this.basicSetting.scale); + jLable.textColor = new Color('FFFFFF') // “京享”二字字体颜色 + ;[name, value].map(t => t.textColor = this.widgetColor); + } + + // #####################顶部内容################### + async setHeaderShow(stack, image) { + const topStack = stack.addStack(); + topStack.centerAlignContent(); + if (image) { + const JDLogo = topStack.addImage(await this.getImageByUrl(this.logo, 'logoImage.png')); + JDLogo.imageSize = new Size(this.basicSetting.logo * this.basicSetting.scale, this.basicSetting.logo * this.basicSetting.scale); + topStack.addSpacer(10 * this.basicSetting.scale); + const JD = topStack.addImage(await this.getImageByUrl(image, 'jingdongImage.png')); + JD.imageSize = new Size(194 * 0.2 * this.basicSetting.scale, 78 * 0.2 * this.basicSetting.scale); + } else { + const imgStack = topStack.addStack(); + if (this.isPlus) { + imgStack.size = new Size(30 * this.basicSetting.scale, 30 * this.basicSetting.scale * 1.039); + imgStack.backgroundImage = await this.getImageByUrl(this.plusBG, 'plusBGImage.png'); + } + const subStack = imgStack.addStack(); + subStack.url = 'openapp.jdmobile://'; + subStack.size = new Size(30 * this.basicSetting.scale, 30 * this.basicSetting.scale); + subStack.cornerRadius = 30 / 2 * this.basicSetting.scale; + subStack.backgroundImage = await this.getImageByUrl(this.basicSetting.customizeAvatar || this.userImage, `userImage_${this.userName}.png`); + if (this.isPlus) { + const userImg = subStack.addImage(await this.getImageByUrl(this.plusFG, 'plusFGImage.png')); + } + } + topStack.addSpacer(); + const jdBean = topStack.addText(this.beanCount.toString()); + jdBean.font = Font.mediumSystemFont(20 * this.basicSetting.scale); + jdBean.textColor = new Color('fa2d19'); // 右上角京豆数颜色 + jdBean.url = 'openapp.jdmobile://virtual?params=%7B%22category%22%3A%22jump%22%2C%22des%22%3A%22m%22%2C%22url%22%3A%22https%3A%2F%2Fbean.m.jd.com%2FbeanDetail%2Findex.action%3FresourceValue%3Dbean%22%7D'; + const desStack = topStack.addStack(); + desStack.layoutVertically(); + desStack.addSpacer(5.5 * this.basicSetting.scale); + const desText = desStack.addText(' 京豆'); + desText.font = Font.mediumSystemFont(10 * this.basicSetting.scale); + desText.textColor = new Color('fa2d19', 0.7); + } + + // #####################京豆收支################### + async setBeanShow(stack, textSize, imageSize) { + const beanStack = stack.addStack(); + // 今日收支 + const yestodayStack = beanStack.addStack(); + yestodayStack.layoutVertically(); + try { + this.bean.ydayIncome = this.rangeTimer[this.yestoday][0] - this.rangeTimer[this.yestoday][1]; + this.bean.ydayExpense = this.rangeTimer[this.yestoday][1]; + this.bean.todayIncome = this.rangeTimer[this.today][0] - this.rangeTimer[this.today][1];; + this.bean.todayExpense = this.rangeTimer[this.today][1]; + } catch (e) { + this.notify(this.name, '\u597d\u50cf\u4f60\u6628\u5929\u6ca1\u6709\u4f7f\u7528\u8be5\u5c0f\u7ec4\u4ef6\uff0c\u8bf7\u91cd\u7f6e\u4eac\u8c46\u6570\u636e'); + } + this.rowBeanCell( + yestodayStack, + this.bean.ydayExpense.toString(), + this.bean.ydayIncome.toString(), + textSize, + '昨日', + ); + beanStack.addSpacer(); + // 京豆图片 + const ddStack = beanStack.addStack(); + ddStack.layoutVertically(); + const ddImg = ddStack.addImage(await this.getImageByUrl(this.beanImg, 'beanImage.png')); + ddImg.imageSize = new Size(imageSize, imageSize); + beanStack.addSpacer(); + // 昨日收支 + const todayStack = beanStack.addStack(); + todayStack.layoutVertically(); + this.rowBeanCell( + todayStack, + this.bean.todayExpense.toString(), + this.bean.todayIncome.toString(), + textSize, + '今日', + ); + } + + // #####################京豆图表################### + async setChartShow(stack, type) { + let labels = [], data = []; + Object.keys(this.rangeTimer).forEach((day) => { + const value = this.rangeTimer[day]; + const arrDay = day.split('-'); + labels.push(arrDay[2]); + if (this.chartSetting.countBean === '收入-支出') + data.push(value[0]); + else data.push(value[0] - value[1]); + }); + let cacheKey = `chart${type}Image${this.isSmall()}_${this.userName}.png`; + let textSize = this.chartSetting.textSize; + let linePadding = this.chartSetting.linePadding; + let barPadding = this.chartSetting.barPadding; + if (config.widgetFamily === 'small') { + data.splice(0, 2); + labels.splice(0, 2); + textSize = this.chartSetting.textSize + 7; + linePadding = this.chartSetting.linePadding + 10; + barPadding = this.chartSetting.barPadding + 5; + } + let chartStr; + switch (type) { + case 2: + chartStr = this.barChart(labels, data, textSize, barPadding, 'bar'); + break; + case 3: + chartStr = this.barChart(labels, data, textSize, barPadding, 'line'); + break; + default: + chartStr = this.lineChart(labels, data, textSize, linePadding); + } + const url = `https://quickchart.io/chart?w=${400}&h=${this.chartSetting.height}&f=png&c=${encodeURIComponent(chartStr)}`; + const chart = await this.getImageByUrl(url, cacheKey, this.cacheChart); + + const chartStack = stack.addStack(); + const chartImage = chartStack.addImage(chart); + const beanDateStack = stack.addStack(); + let showDays = data.length; + for (let i = 0; i < showDays; i++) { + beanDateStack.addSpacer(); + let subStack = beanDateStack.addStack(); + let beanDay = beanDateStack.addText(`${labels[i]}${this.chartSetting.dayText}`); + beanDay.textColor = this.widgetColor; + beanDay.font = new Font('ArialMT', this.chartSetting.daySize * this.basicSetting.scale); + beanDay.textOpacity = 0.8; + beanDateStack.addSpacer(); + } + } + + // #####################物流信息################### + setPackageShow(stack) { + const packageStack = stack.addStack(); + const detailStack = packageStack.addStack(); + detailStack.layoutVertically(); + const titleStack = detailStack.addStack(); + titleStack.centerAlignContent(); + const title = titleStack.addText(this.package.title); + title.lineLimit = 1; + title.font = Font.mediumSystemFont(12 * this.basicSetting.scale); + detailStack.addSpacer(2 * this.basicSetting.scale); + const desc = detailStack.addText(this.package.desc); + desc.lineLimit = 3; + desc.font = Font.regularSystemFont(12 * this.basicSetting.scale); + detailStack.addSpacer(2 * this.basicSetting.scale); + const statusStack = detailStack.addStack(); + const time = statusStack.addText(this.package.time); + statusStack.addSpacer(); + const status = statusStack.addText(this.package.status); + ;[title, desc, time, status].map(t => t.textColor = this.widgetColor); + ;[time, status].map(t => t.font = Font.regularSystemFont(9 * this.basicSetting.scale)); + ;[time, status].map(t => t.textOpacity = 0.7); + } + + // #####################金贴&钢镚################## + async setCoinShow(stack, vertical = false) { + await this.getExtraData(); + const extraDataStack = stack.addStack(); + const jtImage = await this.getImageByUrl(this.jingtieImg, 'jtImage.png'); + const gbImage = await this.getImageByUrl(this.gangbengImg, 'gbImage.png'); + const dataStack = extraDataStack.addStack(); + if (vertical) dataStack.layoutVertically(); + this.rowCell(dataStack, jtImage, this.extra.jingtie.toString(), '金贴'); + if (vertical) extraDataStack.addSpacer(5 * this.basicSetting.scale); + if (!vertical) dataStack.addSpacer(20 * this.basicSetting.scale); + this.rowCell(dataStack, gbImage, this.extra.gangbeng.toString(), '钢镚'); + } + + // #####################京东红包################## + async setRedPackageShow(stack, small = false) { + await this.getRedPackageData(); + const walletImage = await this.getImageByUrl(this.walletImg, 'walletImage.png'); + small ? this.rowSmallWalletCell(stack, walletImage, this.redPackage) : this.rowWalletCell(stack, walletImage, this.redPackage); + } + + // #####################京东白条################## + async setBaitiaoShow(stack, small = false) { + const baitiaoImage = await this.getImageByUrl(this.baitiaoImg, 'baitiaoImage.png'); + small ? this.rowSmallWalletCell(stack, baitiaoImage, this.baitiao) : this.rowWalletCell(stack, baitiaoImage, this.baitiao); + } + + rowCell(stack, image, value, title) { + const rowStack = stack.addStack(); + rowStack.centerAlignContent(); + const rowImage = rowStack.addImage(image); + rowImage.imageSize = new Size(13 * this.basicSetting.scale, 13 * this.basicSetting.scale); + rowStack.addSpacer(); + const rowValue = rowStack.addText(value); + rowValue.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + rowStack.addSpacer(); + const rowTitle = rowStack.addText(title); + rowTitle.font = Font.regularSystemFont(13 * this.basicSetting.scale); + ;[rowValue, rowTitle].map(t => t.textColor = this.widgetColor); + } + + rowBeanCell(stack, min, add, textSize, label) { + const rowOne = stack.addStack(); + const labelText = rowOne.addText(label); + labelText.font = Font.regularSystemFont(10 * this.basicSetting.scale); + labelText.textOpacity = 0.7; + const rowTwo = stack.addStack(); + const rowNumber = rowTwo.addText(add); + rowNumber.font = Font.lightSystemFont(textSize); + if (min < 0) { + const rowThree = stack.addStack(); + const minText = rowThree.addText(min); + minText.font = Font.mediumSystemFont(10 * this.basicSetting.scale); + minText.textColor = new Color('fa2d19'); // 支出京豆颜色 + } + ;[labelText, rowNumber].map(t => t.textColor = this.widgetColor); + } + + rowWalletCell(stack, image, data) { + const stackOne = stack.addStack(); + stackOne.centerAlignContent(); + const stackImage = stackOne.addImage(image); + stackImage.imageSize = new Size(127 * 0.17 * this.basicSetting.scale, 75 * 0.17 * this.basicSetting.scale); + stackOne.addSpacer(5 * this.basicSetting.scale); + const title = stackOne.addText(data.title); + title.font = Font.regularSystemFont(13 * this.basicSetting.scale); + stackOne.addSpacer(); + const number = stackOne.addText(`${data.number}`); + number.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + stackOne.addSpacer(); + const desc = stackOne.addText(data.desc); + desc.font = Font.regularSystemFont(10 * this.basicSetting.scale); + desc.textOpacity = 0.7; + ;[title, number, desc].map(t => t.textColor = this.widgetColor); + } + + rowSmallWalletCell(stack, image, data) { + const stackOne = stack.addStack(); + stackOne.centerAlignContent(); + const stackImage = stackOne.addImage(image); + stackImage.imageSize = new Size(127 * 0.17 * this.basicSetting.scale, 75 * 0.17 * this.basicSetting.scale); + stackOne.addSpacer(); + const number = stackOne.addText(`${data.number}`); + number.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + stack.addSpacer(5 * this.basicSetting.scale); + const stackTwo = stack.addStack(); + stackTwo.centerAlignContent(); + const title = stackTwo.addText(data.title); + title.font = Font.regularSystemFont(13 * this.basicSetting.scale); + stackTwo.addSpacer(); + const desc = stackTwo.addText(data.desc); + desc.font = Font.regularSystemFont(10 * this.basicSetting.scale); + desc.textOpacity = 0.7; + ;[number, title, desc].map(t => t.textColor = this.widgetColor); + } + + init = async () => { + try { + let beanCacheKey = `beanData${this.isSmall()}_${this.userName}`; + let beanCacheData = !this.loadStringCache(beanCacheKey) ? {} : JSON.parse(this.loadStringCache(beanCacheKey)); + let beanCache = beanCacheData.data ? beanCacheData.data.assetInfo.beanNum : 0; + await this.TotalBean(); + await this.wxData(); + console.log(`京豆数据:${beanCache}`); + console.log(`京豆数据:${this.beanCount}`); + + if (!this.cookie) return; + if (Keychain.contains(this.CACHE_KEY)) { + this.rangeTimer = JSON.parse(Keychain.get(this.CACHE_KEY)); + if (this.rangeTimer.hasOwnProperty(this.today) && beanCache !== 0 && beanCache == this.beanCount) { + this.cacheChart = this.funcSetting.alwaysRefreshChart ==='打开' ? false : true; + console.log('京豆数据:无变化,使用缓存数据'); + return; + } + + this.rangeTimer[this.today] = [0, 0]; + const timerKeys = Object.keys(this.rangeTimer); + if (timerKeys.length > this.maxDays) { + for (let i = 0; i < timerKeys.length - this.maxDays; i++) { + delete this.rangeTimer[timerKeys[i]]; + } + Keychain.set(this.CACHE_KEY, JSON.stringify(this.rangeTimer)); + } + + this.timerKeys = [this.today]; + } else { + this.rangeTimer = this.getDay(5); + this.timerKeys = Object.keys(this.rangeTimer); + } + await this.getAmountData(); + } catch (e) { + console.log(e); + } + }; + + getAmountData = async () => { + let i = 0, + page = 1; + do { + const response = await this.getJingBeanBalanceDetail(page); + const result = response.code === '0'; + console.log(`第${page}页:${result ? '请求成功' : '请求失败'}`); + if (response.code === '3') { + i = 1; + this.notify(this.name, response.message) + console.log(response); + } + if (response && result) { + page++; + let detailList = response.jingDetailList; + if (detailList && detailList.length > 0) { + for (let item of detailList) { + const dates = item.date.split(' '); + if (this.timerKeys.indexOf(dates[0]) > -1) { + const amount = Number(item.amount); + this.rangeTimer[dates[0]][0] += amount; + if (amount < 0) + this.rangeTimer[dates[0]][1] += amount; + } else { + i = 1; + Keychain.set(this.CACHE_KEY, JSON.stringify(this.rangeTimer)); + break; + } + } + } + } + } while (i === 0); + } + + getDay(dayNumber) { + let data = {}; + let i = dayNumber; + do { + const today = new Date(); + const year = today.getFullYear(); + const targetday_milliseconds = today.getTime() - 1000 * 60 * 60 * 24 * i; + today.setTime(targetday_milliseconds); + let month = today.getMonth() + 1; + month = month >= 10 ? month : `0${month}`; + let day = today.getDate(); + day = day >= 10 ? day : `0${day}`; + data[`${year}-${month}-${day}`] = [0, 0]; + i--; + } while (i >= 0); + return data; + } + + TotalBean = async () => { + const dataName = '京豆数据'; + let userCache = `beanData${this.isSmall()}`; + const url = 'https://me-api.jd.com/user_new/info/GetJDUserInfoUnion'; + const options = { + headers: { + cookie: this.cookie, + }, + }; + const response = await this.httpRequest(dataName, url, true, options, userCache); + try { + if (response.retcode === '0' && response['data']) { + this.beanCount = response.data.assetInfo.beanNum; + this.userImage = response.data.userInfo.baseInfo.headImageUrl.replace(/big/, 'mid') || 'https://img11.360buyimg.com/jdphoto/s120x120_jfs/t21160/90/706848746/2813/d1060df5/5b163ef9N4a3d7aa6.png'; + this.nickName = response.data.userInfo.baseInfo.nickname; + this.isPlus = response.data.userInfo.isPlusVip === '1' ? true : false; + } else { + this.notify(this.name, response.msg); + console.log('京豆数据:获取失败,' + response.msg) + } + } catch (e) { + console.log(e); + console.log('京豆数据:获取失败,') + } + } + + wxData = async () => { + const dataName = '微信数据'; + let userCache = `wxData${this.isSmall()}`; + const url = 'https://wxapp.m.jd.com/kwxhome/myJd/home.json?&useGuideModule=0&bizId=&brandId=&fromType=wxapp'; + const options = { + headers: { + cookie: this.cookie, + }, + }; + const response = await this.httpRequest(dataName, url, true, options, userCache); + try { + if (response['user']) { + this.jValue = response.user.uclass.replace(/[^0-9]/ig, ''); + } else { + this.notify(this.name, response.msg); + console.log('微信数据:获取失败,' + response.msg) + } + } catch (e) { + console.log(e); + } + } + + getJingBeanBalanceDetail = async (page) => { + try { + const options = { + url: `https://bean.m.jd.com/beanDetail/detail.json`, + body: `page=${page}`, + headers: { + 'X-Requested-With': `XMLHttpRequest`, + Connection: `keep-alive`, + 'Accept-Encoding': `gzip, deflate, br`, + 'Content-Type': `application/x-www-form-urlencoded; charset=UTF-8`, + Origin: `https://bean.m.jd.com`, + 'User-Agent': `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15`, + Cookie: this.cookie, + Host: `bean.m.jd.com`, + Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, + 'Accept-Language': `zh-cn`, + Accept: `application/json, text/javascript, */*; q=0.01`, + }, + }; + let params = {...options, method: 'POST'}; + let request = new Request(params.url); + Object.keys(params).forEach((key) => { + request[key] = params[key]; + }); + return await request.loadJSON(); + } catch (e) { + console.log(e); + } + } + + getExtraData = async () => { + const JTDataName = '金贴数据'; + const JTUrl = 'https://ms.jr.jd.com/gw/generic/uc/h5/m/mySubsidyBalance'; + const GBDataName = '钢镚数据'; + const GBUrl = 'https://coin.jd.com/m/gb/getBaseInfo.html'; + const options = { + headers: { + cookie: this.cookie, + Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', + }, + }; + try { + const JTData = await this.httpRequest(JTDataName, JTUrl, true, options, 'jintieData'); + const GBData = await this.httpRequest(GBDataName, GBUrl, true, options, 'gangbengData'); + if (JTData.resultCode === 0) { + this.extra.jingtie = JTData.resultData.data['balance']; + } else { + this.notify(this.name, JTdata.resultMsg); + console.log('金贴数据:获取失败,' + JTdata.resultMsg); + }; + if (GBData.gbBalance) this.extra.gangbeng = GBData.gbBalance; + } catch(e) { + console.log(e); + } + } + + getPackageData = async () => { + const dataName = '包裹数据'; + const url ='https://wq.jd.com/bases/wuliudetail/notify?sceneval=2&sceneval=2&g_login_type=1&callback'; + const options = { + headers: { + cookie: this.cookie, + Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'packageData'); + if (data.errCode == 0 && data['dealLogList']) { + console.log('包裹数据:获取成功'); + console.log(`包裹数据:您有${data['dealLogList'].length}个包裹`); + if (data['dealLogList'].length > 0) { + let item = data.dealLogList[Math.floor(Math.random() * data['dealLogList'].length)] + this.package.number = data.dealLogList.length; + this.package.title = item['name']; + this.package.desc = item['wlStateDesc']; + this.package.time = item['createTime']; + this.package.status = item['stateName']; + } + } else { + console.log('包裹数据:获取失败'); + } + } catch (e) { + console.log(e); + } + } + + getRedPackageData = async () => { + const dataName = '红包数据'; + const url = + 'https://wq.jd.com/user/info/QueryUserRedEnvelopesV2?type=1&orgFlag=JD_PinGou_New&page=1&cashRedType=1&redBalanceFlag=1&channel=3&sceneval=2&g_login_type=1'; + const options = { + headers: { + cookie: this.cookie, + Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'redPackageData'); + if (data.errcode === 0) { + this.redPackage.number = data.data.balance ? data.data.balance : 0; + if (data.data.expiredBalance && data.data.expiredBalance !== '') this.redPackage.desc = `即将过期${data.data.expiredBalance}`; + } else { + this.notify(this.name, data.msg); + console.log('红包数据:获取失败,' + data.msg); + } + } catch (e) { + console.log(e); + } + } + + getJValue = async () => { + const dataName = '京享数据'; + const url = 'https://vip.m.jd.com/scoreDetail/current'; + const options = { + headers: { + cookie: this.cookie, + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'JValue'); + if (data.code === 0) { + this.jValue = data.model.scoreDescription.userScore.score; + } else { + console.log('京享数据:获取失败'); + }; + } catch (e) { + console.log(e); + } + } + + getBaitiaoData = async () => { + const dataName = '白条数据'; + const url = 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew'; + const options = { + body: 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}', + headers: { + cookie: this.cookie, + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'baitiaoData', 'POST', false); + if (data.resultCode !== 0) { + return this.notify(this.name, data['resultMsg']); + } + this.baitiao.title = data['resultData']['data']['bill']['title']; + this.baitiao.number = data['resultData']['data']['bill']['amount'].replace(/,/g, ''); + this.baitiao.desc = data['resultData']['data']['bill']['buttonName'].replace(/最近还款日/, ''); + } catch (e) { + console.log(e); + } + } + + getFruitData = async () => { + const dataName = '东东农场'; + const url = 'https://api.m.jd.com/client.action?functionId=initForFarm'; + const options = { + body: 'body=version:4&appid=wh5&clientVersion=9.1.0', + headers: { + 'User-Agent': 'jdapp;iPhone;9.2.2;14.2;%E4%BA%AC%E4%B8%9C/9.2.2 CFNetwork/1206 Darwin/20.1.0', + 'Content-Type': 'application/x-www-form-urlencoded', + 'cookie': this.cookie, + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'FruitData', 'POST', false); + if (data.msg && data.msg == 'not login') { + this.fruitState = "X"; + } + else if (data.farmUserPro.treeState == 2 || data.farmUserPro.treeState == 3) { + this.fruitState = "100"; + } + else if (data.farmUserPro.treeState == 0) { + this.fruitState = "X"; + } + else { + this.fruitState = Math.floor((data.farmUserPro.treeEnergy / data.farmUserPro.treeTotalEnergy) * 100).toString(); + } + } catch (e) { + console.log(e); + } + } + + getImageByUrl = async(url, cacheKey, useCache = true, logable = true) => { + if (this.CACHES.indexOf(cacheKey) < 0) { + this.CACHES.push(cacheKey); + this.settings.CACHES = this.CACHES; + this.saveSettings(false); + } + if (useCache) { + const cacheImg = this.loadImgCache(cacheKey); + if (cacheImg != undefined && cacheImg != null) { + if (logable) console.log(`使用缓存:${cacheKey}`); + return this.loadImgCache(cacheKey); + } + } + + try { + if (logable) console.log(`在线请求:${cacheKey}`); + const req = new Request(url); + const imgData = await req.load(); + const img = Image.fromData(imgData); + this.saveImgCache(cacheKey, img); + return img; + } catch (e) { + console.error(`图片加载失败:${e}`); + let cacheImg = this.loadImgCache(cacheKey); + if (cacheImg != undefined) { + console.log(`使用缓存图片:${cacheKey}`); + return cacheImg; + } + console.log(`使用预设图片`); + let ctx = new DrawContext(); + ctx.size = new Size(80, 80); + ctx.setFillColor(Color.darkGray()); + ctx.fillRect(new Rect(0, 0, 80, 80)); + return await ctx.getImage(); + } + } + + saveImgCache(cacheKey, img) { + if (!this.fm.fileExists(this.cachePath)) { + this.fm.createDirectory(this.cachePath, true); + }; + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + this.fm.writeImage(cacheFile, img); + } + + loadImgCache(cacheKey) { + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + const fileExists = this.fm.fileExists(cacheFile); + let img = undefined; + if (fileExists) { + img = Image.fromFile(cacheFile); + } + return img; + } + + httpRequest = async(dataName, url, json = true, options, key, method = 'GET', logable = this.funcSetting.logable === '打开') => { + let cacheKey = `${key}_${this.userName}`; + if (this.CACHES.indexOf(cacheKey) < 0) { + this.CACHES.push(cacheKey); + this.settings.CACHES = this.CACHES; + this.saveSettings(false); + } + const localCache = this.loadStringCache(cacheKey); + const lastCacheTime = this.getCacheModificationDate(cacheKey); + const timeInterval = Math.floor((this.getCurrentTimeStamp() - lastCacheTime) / 60); + console.log( + `${dataName}:缓存${timeInterval}分钟前,有效期${this.basicSetting.interval}分钟,${localCache.length}`); + if (timeInterval < this.basicSetting.interval && localCache != null && localCache.length > 0) { + console.log(`${dataName}:读取缓存`); + return json ? JSON.parse(localCache) : localCache; + } + let data = null; + try { + console.log(`${dataName}:在线请求`); + let req = new Request(url); + req.method = method; + Object.keys(options).forEach((key) => { + req[key] = options[key]; + }); + data = await (json ? req.loadJSON() : req.loadString()); + } catch (e) { + console.error(`${dataName}:请求失败:${e}`); + } + if (!data && localCache != null && localCache.length > 0) { + console.log(`${dataName}:获取失败,读取缓存`); + return json ? JSON.parse(localCache) : localCache; + } + this.saveStringCache(cacheKey, json ? JSON.stringify(data) : data); + if (logable) { + console.log(`${dataName}:在线请求响应数据:${JSON.stringify(data)}`); + } + return data; + } + + loadStringCache(cacheKey) { + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + const fileExists = this.fm.fileExists(cacheFile); + let cacheString = ''; + if (fileExists) { + cacheString = this.fm.readString(cacheFile); + } + return cacheString; + } + + saveStringCache(cacheKey, content) { + if (!this.fm.fileExists(this.cachePath)) { + this.fm.createDirectory(this.cachePath, true); + }; + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + this.fm.writeString(cacheFile, content); + } + + getCacheModificationDate(cacheKey) { + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + const fileExists = this.fm.fileExists(cacheFile); + if (fileExists) { + return this.fm.modificationDate(cacheFile).getTime() / 1000; + } else { + return 0; + } + } + + getCurrentTimeStamp() { + return new Date().getTime() / 1000; + } + + timeFormat(time) { + let date; + if (time) { + date = new Date(time); + } else { + date = new Date(); + } + return ((date.getMonth() + 1) >= 10 ? (date.getMonth() + 1) : '0' + (date.getMonth() + 1)) + '月' + (date.getDate() >= 10 ? date.getDate() : '0' + date.getDate()) + '日'; + } + + async updateCheck(version){ + let data; + try { + let updateCheck = new Request('https://raw.githubusercontent.com/anker1209/Scriptable/main/upcoming.json'); + data = await updateCheck.loadJSON(); + if (data.version != version) { + let updata = new Alert(); + updata.title = `有新版 ${data.version} 可用`; + updata.addAction('去Github更新'); + updata.addAction('网页版商店更新'); + updata.addCancelAction('稍后'); + updata.message = '\n更新说明:\n' + data.notes + '\n\n点击相应按钮更新脚本'; + let id = await updata.present(); + if (id == 0) { + Safari.openInApp('https://raw.githubusercontent.com/anker1209/Scriptable/main/scripts/JD-in-one-v2.js'); + } else if (id == 1) { + Safari.openInApp('http://scriptablejs.gitee.io/store/#/menu/myInfo'); + } else { + return; + } + } else { + let updata = new Alert(); + updata.title = '暂无更新'; + updata.addCancelAction('好的'); + updata.message = `\n当前版本 ${version} 为最新版本`; + await updata.present(); + } + } catch(e) { + console.log(e); + } + } + + async faqTable() { + const table = new UITable(); + table.showSeparators = false; + let data; + try { + let faq = new Request('https://raw.githubusercontent.com/anker1209/Scriptable/main/faq.json'); + data = await faq.loadJSON(); + let info = new UITableRow(); + info.height = parseFloat(data.height); + let desc = info.addText(data.update, data.desc); + desc.subtitleColor = Color.blue(); + desc.titleFont = Font.mediumSystemFont(14); + desc.subtitleFont = Font.systemFont(14); + table.addRow(info); + for (let i = 0; i < data.data.length; i++) { + let header = new UITableRow(); + header.backgroundColor = Color.dynamic(new Color('F5F5F5'), new Color('000000'));; + let heading = header.addText(data.data[i].name) + heading.titleFont = Font.mediumSystemFont(17); + heading.centerAligned(); + table.addRow(header); + data.data[i].item.forEach((faq) => { + let row = new UITableRow(); + row.height = parseFloat(faq['height']); + let rowtext = row.addText(faq['question'], faq['answer']); + rowtext.titleFont = Font.mediumSystemFont(16); + rowtext.titleColor = Color.blue(); + rowtext.subtitleFont = Font.systemFont(14); + rowtext.subtitleColor = Color.dynamic(new Color('000000', 0.7), new Color('ffffff', 0.7)); + table.addRow(row); + }); + } + } catch (e) { + console.log(e); + } + await table.present(); + } + + async settingCategory(table, item, outfit, category) { + let header = new UITableRow(); + let heading = header.addText(outfit) + heading.titleFont = Font.mediumSystemFont(17); + heading.centerAligned(); + table.addRow(header); + item.forEach((data) => { + Object.keys(data.option).forEach((key) => { + let row = new UITableRow(); + let rowIcon = row.addImageAtURL(data['icon']); + rowIcon.widthWeight = 100; + let rowtext = row.addText(data['title']); + rowtext.widthWeight = 400; + let rowNumber = row.addText(`${this.settings[category][key]} >`); + rowNumber.widthWeight = 500; + rowNumber.rightAligned(); + rowNumber.titleColor = Color.gray(); + rowNumber.titleFont = Font.systemFont(16); + rowtext.titleFont = Font.systemFont(16); + row.dismissOnSelect = false; + row.onSelect = async () => { + if (data.type == 'text') { + await this.alertInput(data['title'], data['desc'], category, data['option']); + } else if (data.type == 'menu') { + await this.showAlert(data['title'], data['desc'], data['menu'], category, key,); + } + await this.tableContent(table); + } + table.addRow(row); + }); + }); + table.reload(); + } + + async tableContent(table) { + const basic = [ + {type: 'text', title: '全局缩放比例', desc: '排版溢出、显示不全的请优先调低此数,建议递减0.05调整,如0.95、0.90……\n\n缺省值:1.00', option: {scale: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/scale.png'}, + {type: 'text', title: '京东标志大小', desc: '京东logo(形象狗)大小\n\n缺省值:30', option: {logo: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/logo.png'}, + {type: 'text', title: '用户头像大小', desc: '⚠️注意:若要修改头像,请在京东app上传后将缓存清除再运行脚本。\n\n缺省值:69', option: {userImage: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/userImage.png'}, + {type: 'text', title: '左侧栏宽度', desc: '左侧用户信息栏整体宽度\n\n缺省值:103', option: {userStack: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/userStack.png'}, + {type: 'text', title: '左右栏间距', desc: '左侧用户信息栏与右侧京豆数据间距\n\n缺省值:25', option: {division: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/division.png'}, + {type: 'text', title: '缓存时间', desc: '数据请求间隔时间\n请设置合适时间,避免频繁访问接口数据以及加载缓慢。单位:分钟\n\n缺省值:10', option: {interval: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/interval.png'}, + {type: 'menu', title: '缓存位置', desc: '将缓存保存在Local或者iCloud。\n\n缺省值:Local', option: {directory: ''}, menu: ['Local', 'iCloud'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/directory.png'}, + {type: 'text', title: '自定义昵称', desc: '自定义用户信息栏的昵称名称,\n留空将显示京东账号昵称。\n\n注意:单脚本多账户若使用自定义昵称,所有账户将同时显示此昵称,如需单独自定义昵称,请复制脚本单独设置。', option: {customizeName: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/customizeName.png'}, + {type: 'text', title: '自定义头像', desc: '自定义用户信息栏的头像,\n留空将显示京东APP头像。\n\n注意:单脚本多账户若使用自定义头像,所有账户将同时显示此头像,如需单独自定义头像,请复制脚本单独设置。', option: {customizeAvatar: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/customizeAvatar.png'}, + {type: 'menu', title: '小组件显示内容', desc: '\n缺省值:京豆、钱包数据', option: {smallShowType: ''}, menu: ['京豆、钱包数据', '个人信息'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/smallShowType.png'}, + {type: 'menu', title: '钱包显示类型', desc: '若要显示钱包内容,白条需关闭或者白条打开的情况下无待还白条。\n\n缺省值:红包', option: {walletShowType: ''}, menu: ['红包', '钢镚和金贴'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/walletShowType.png'}, + ]; + const chart = [ + {type: 'text', title: '图表高度', desc: '京豆数据未与日期对齐的,\n请调低此数值\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:130', option: {height: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/height.png'}, + {type: 'text', title: '日期文字大小', desc: '京豆图表底部日期文字大小\n\n缺省值:9', option: {daySize: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/daySize.png'}, + {type: 'text', title: '日期文字后缀', desc: '京豆图表底部日期文字后缀', option: {dayText: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/dayText.png'}, + {type: 'text', title: '京豆数文字大小', desc: '京豆图表数据文字大小\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:18', option: {textSize: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textSize.png'}, + {type: 'text', title: '京豆数白天颜色', desc: '⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:999999', option: {textDayColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textDayColor.png'}, + {type: 'text', title: '京豆数晚上颜色', desc: '⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:999999', option: {textNightColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textNightColor.png'}, + {type: 'text', title: '折线图线条颜色', desc: '支持渐变色,每个颜色之间以英文逗号分隔,颜色值必须带“#”。\n\n缺省值:#FA6859', option: {lineColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/lineColor.png'}, + {type: 'text', title: '折线图表顶边距', desc: '京豆折线图顶边距\n京豆数据在顶部被剪切显示不全的,\n请调高此数值。\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:15', option: {linePadding: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/linePadding.png'}, + {type: 'text', title: '柱状图表顶边距', desc: '京豆柱状图和曲线面积图顶边距\n京豆数据在顶部被剪切显示不全的,\n请调高此数值。\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:5', option: {barPadding: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/barPadding.png'}, + {type: 'menu', title: '小组件图表类型', desc: '\n缺省值:双日视图', option: {smallShowType: ''}, menu: ['双日视图', '折线图表', '柱状图表', '曲线面积图'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/smallShowType2.png'}, + {type: 'menu', title: '中组件图表类型', desc: '\n缺省值:双日视图', option: {showType: ''}, menu: ['双日视图', '折线图表', '柱状图表', '曲线面积图'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showType.png'}, + {type: 'menu', title: '每日京豆数计算', desc: '\n缺省值:收入-支出', option: {countBean: ''}, menu: ['收入-支出', '收入'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/countBean.png'}, + {type: 'menu', title: '多彩柱状图', desc: '设置为打开时仅对柱状图表生效\n\n缺省值:关闭', option: {colorful: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/colorful.png'}, + ]; + const func = [ + {type: 'menu', title: '白条信息', desc: '关闭或者打开后无待还白条的情况下,\n会显示基础设置里选择的钱包内容。\n\n缺省值:打开', option: {showBaitiao: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showBaitiao.png'}, + {type: 'menu', title: '包裹信息', desc: '只有中组件显示一条物流信息,\n若无物流信息会显示图表设置里选择的图表类型。\n\n缺省值:关闭', option: {showPackage: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showPackage.png'}, + {type: 'menu', title: '农场进度', desc: '显示东东农场种植进度。\n\n缺省值:打开', option: {showFruit: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showFruit.png'}, + {type: 'menu', title: '运行日志', desc: '出现数据异常请将此值设为true,\n查看运行日志。\n\n⚠️注意:\n查看运行日志需将缓存时间更改为0。\n\n缺省值:关闭', option: {logable: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/logable.png'}, + {type: 'menu', title: '刷新图表', desc: '打开,每次刷新组件会随机刷新图表颜色(仅柱状图表和曲线面积图);关闭,则只有在京豆数据有变化的情况下刷新图表颜色及数据。建议在排版调整没有问题后,设置为关闭。设置为打开会加长数据载入时间。\n\n⚠️注意:图表设置选项里修改图表高度、颜色、文字大小、顶边距需打开此选项以查看即时反馈。\n\n缺省值:打开', option: {alwaysRefreshChart: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/alwaysRefreshChart.png'}, + ]; + table.removeAllRows(); + let topRow = new UITableRow(); + let leftText = topRow.addButton('教程'); + leftText.widthWeight = 0.25; + leftText.onTap = async () => { + await Safari.open('https://github.com/anker1209/Scriptable#jd_in_one'); + } + let faqText = topRow.addButton('常见问题'); + faqText.widthWeight = 0.25; + faqText.leftAligned(); + faqText.onTap = async () => { + await this.faqTable(); + } + let versionText = topRow.addButton('版本检测'); + versionText.widthWeight = 0.25; + versionText.rightAligned(); + versionText.onTap = async () => { + await this.updateCheck(this.version); + } + let rightText = topRow.addButton('电报群'); + rightText.widthWeight = 0.25; + rightText.rightAligned(); + rightText.onTap = async () => { + await Safari.open('https://t.me/Scriptable_JS'); + } + table.addRow(topRow); + + let header = new UITableRow(); + let heading = header.addText('重置设置') + heading.titleFont = Font.mediumSystemFont(17); + heading.centerAligned(); + table.addRow(header); + let row1 = new UITableRow(); + let rowtext1 = row1.addText('重置缓存','若需要修改头像或数据显示错误,尝试此操作'); + rowtext1.titleFont = Font.systemFont(16); + rowtext1.subtitleFont = Font.systemFont(12); + rowtext1.subtitleColor = new Color('999999'); + row1.dismissOnSelect = false; + row1.onSelect = async () => { + const options = ['取消', '重置']; + const message = '所有在线请求的数据缓存将会被清空'; + const index = await this.generateAlert(message, options); + if (index === 0) return; + this.fm.remove(this.cachePath); + delete this.settings['CACHES']; + this.saveSettings(); + } + table.addRow(row1); + let row2 = new UITableRow(); + let rowtext2 = row2.addText('重置京豆数据','若京豆数据缺失或显示有误,尝试此操作'); + rowtext2.titleFont = Font.systemFont(16); + rowtext2.subtitleFont = Font.systemFont(12); + rowtext2.subtitleColor = new Color('999999'); + row2.dismissOnSelect = false; + row2.onSelect = async () => { + const options = ['取消', '重置']; + const message = '若缺少京豆数据或显示为0(双日视图或图表的京豆数)采用此操作。京豆数据重置后,将会重新抓取近6天的京豆明细。请勿频繁使用,会产生大量数据'; + const index = await this.generateAlert(message, options); + if (index === 0) return; + Keychain.remove(this.settings.CACHE_KEY); + delete this.settings.CACHE_KEY; + this.saveSettings(); + } + table.addRow(row2); + let row3 = new UITableRow(); + let rowtext3 = row3.addText('重置设置参数','设置参数绑定脚本文件名,请勿随意更改脚本文件名'); + rowtext3.titleFont = Font.systemFont(16); + rowtext3.subtitleFont = Font.systemFont(12); + rowtext3.subtitleColor = new Color('999999'); + row3.dismissOnSelect = false; + row3.onSelect = async () => { + const options = ['取消', '重置']; + const message = '本菜单里的所有设置参数将会重置为默认值,重置后请重新打开设置菜单'; + const index = await this.generateAlert(message, options); + if (index === 0) return; + delete this.settings['basicSetting']; + delete this.settings['chartSetting']; + delete this.settings['funcSetting']; + this.saveSettings(); + } + table.addRow(row3); + await this.settingCategory(table, basic, '基础设置', 'basicSetting'); + await this.settingCategory(table, chart, '图表设置', 'chartSetting'); + await this.settingCategory(table, func, '功能设置', 'funcSetting'); + } + + async editSettings() { + const table = new UITable(); + table.showSeparators = true; + await this.tableContent(table); + await table.present(true); + } + + alertInput = async (title, desc, category, opt = {}) => { + const a = new Alert(); + a.title = title; + a.message = !desc ? '' : desc; + let key = Object.keys(opt)[0]; + a.addTextField(key, `${this.settings[category][key]}`); + a.addAction('确定'); + a.addCancelAction('取消'); + const id = await a.presentAlert(); + if (id === -1) return; + this.settings[category][key] = a.textFieldValue(0); + this.saveSettings(); + }; + + async showAlert(title, message, options, category, key) { + let alert = new Alert(); + alert.title = title; + alert.message = message; + alert.addCancelAction('取消') + for (const option of options) { + alert.addAction(option); + }; + let id = await alert.presentAlert(); + if (id === -1) return; + this.settings[category][key] = options[id]; + this.saveSettings(); + } + + run = (filename, args) => { + if(!this.settings.basicSetting) this.settings.basicSetting = this.basicSetting; + Object.keys(this.basicSetting).forEach((key) => { + if(!this.settings.basicSetting.hasOwnProperty(key)) + this.settings['basicSetting'][key] = this.basicSetting[key]; + }); + if(!this.settings.chartSetting) this.settings.chartSetting = this.chartSetting; + Object.keys(this.chartSetting).forEach((key) => { + if(!this.settings.chartSetting.hasOwnProperty(key)) + this.settings['chartSetting'][key] = this.chartSetting[key]; + }); + if(!this.settings.funcSetting) this.settings.funcSetting = this.funcSetting; + Object.keys(this.funcSetting).forEach((key) => { + if(!this.settings.funcSetting.hasOwnProperty(key)) + this.settings['funcSetting'][key] = this.funcSetting[key]; + }); + if(!this.settings.CACHES) this.settings.CACHES = []; + this.CACHES = this.settings.CACHES; + if (this.settings['basicSetting']['directory'] === 'iCloud') this.fm = FileManager.iCloud(); + this.cachePath = this.fm.joinPath(this.fm.documentsDirectory(), this.CACHE_FOLDER); + + if (config.runsInApp) { + this.registerAction('参数配置', this.editSettings, 'https://gitee.com/anker1209/image/raw/master/jd/setting.png'); + this.registerAction('账号设置', async () => { + const index = await this.generateAlert('设置账号信息', [ + '网站登录', + '手动输入', + ]); + if (index === 0) { + await this.jdWebView(); + } else { + await this.setAlertInput('账号设置', '京东账号cookie\n\n⚠️\n用户名和cookie必须输入!\n多账号注意用户名不要重复!', { + username: '用户名,必须输入!多账号勿重复!', + cookie: 'Cookie', + }); + } + }, 'https://gitee.com/anker1209/image/raw/master/jd/account.png'); + this.registerAction('代理缓存', this.actionSettings, 'https://gitee.com/anker1209/image/raw/master/jd/boxjs.png'); + this.registerAction('基础设置', this.setWidgetConfig, 'https://gitee.com/anker1209/image/raw/master/jd/preferences.png'); + } + Object.keys(this.settings['basicSetting']).forEach((key) => { + if (key == 'customizeName' || key == 'customizeAvatar' || key == 'smallShowType' || key == 'walletShowType' || key == 'directory') { + this.basicSetting[key] = this.settings['basicSetting'][key]; + } else if (!isNaN(this.settings['basicSetting'][key])) { + this.basicSetting[key] = parseFloat(this.settings['basicSetting'][key]); + } + }); + Object.keys(this.settings['chartSetting']).forEach((key) => { + if (key == 'textDayColor' || key == 'textNightColor' || key =='showType' || key == 'smallShowType' || key == 'countBean' || key == 'colorful' || key == 'lineColor' || key == 'dayText') { + this.chartSetting[key] = this.settings['chartSetting'][key]; + } else if (!isNaN(this.settings['chartSetting'][key])) { + this.chartSetting[key] = parseFloat(this.settings['chartSetting'][key]); + } + }); + Object.keys(this.settings['funcSetting']).forEach((key) => { + this.funcSetting[key] = this.settings['funcSetting'][key]; + }); + + + }; + + jdWebView = async () => { + const webView = new WebView(); + const url = + 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; + await webView.loadURL(url); + await webView.present(true); + const req = new Request( + 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', + ); + req.method = 'POST'; + req.body = + 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; + await req.loadJSON(); + const cookies = req.response.cookies; + const account = {username: '', cookie: ''}; + const cookie = []; + cookies.forEach((item) => { + const value = `${item.name}=${item.value}`; + if (item.name === 'pt_key') cookie.push(value); + if (item.name === 'pt_pin') { + account.username = item.value; + cookie.push(value); + } + }); + account.cookie = cookie.join('; '); + console.log(account); + + if (account.cookie) { + this.settings = {...this.settings, ...account}; + this.saveSettings(false); + console.log(`${this.name}: cookie获取成功,请关闭窗口!`); + this.notify(this.name, 'cookie获取成功,请关闭窗口!'); + } + }; + + _loadJDCk = async () => { + try { + const CookiesData = await this.getCache('CookiesJD'); + if (CookiesData) { + this.CookiesData = this.transforJSON(CookiesData); + } + const CookieJD = await this.getCache('CookieJD'); + if (CookieJD) { + const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; + const ck1 = { + cookie: CookieJD, + userName, + }; + this.CookiesData.push(ck1); + } + const Cookie2JD = await this.getCache('CookieJD2'); + if (Cookie2JD) { + const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; + const ck2 = { + cookie: Cookie2JD, + userName, + }; + this.CookiesData.push(ck2); + } + return true; + } catch (e) { + console.log(e); + this.CookiesData = []; + return false; + } + }; + + async actionSettings() { + try { + const table = new UITable(); + if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; + // 如果是节点,则先远程获取 + this.settings.cookieData = this.CookiesData; + this.saveSettings(false); + this.CookiesData.map((t, index) => { + const r = new UITableRow(); + r.addText(`parameter:${index} ${t.userName}`); + r.onSelect = (n) => { + this.settings.username = t.userName; + this.settings.cookie = t.cookie; + this.saveSettings(); + }; + table.addRow(r); + }); + let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; + if (this.settings.cookie) { + body += ',或者使用当前选中Ck:' + this.settings.username; + } + this.notify(this.name, body); + table.present(false); + } catch (e) { + this.notify( + this.name, + '', + 'BoxJS 数据读取失败,请点击通知查看教程', + 'https://chavyleung.gitbook.io/boxjs/awesome/videos', + ); + } + } + + + async getCookie() { + this.JDindex = + typeof args.widgetParameter === 'string' + ? parseInt(args.widgetParameter) + : false; + let _md5 = this.md5(module.filename + this.en); + if (this.funcSetting.logable === '打开') console.log('当前配置内容:' + JSON.stringify(this.settings)); + await this._loadJDCk(); + this.JDindex = + typeof args.widgetParameter === "string" + ? parseInt(args.widgetParameter) + : false; + try { + if (this.JDindex !== false && this.JDindex + 1 > 0) { + this.cookie = this.CookiesData[this.JDindex]['cookie']; + this.userName =this.CookiesData[this.JDindex]["userName"]; + } else { + const cacheCookie = this.CookiesData.find(item=>item.userName === this.settings.username) || {}; + this.userName = cacheCookie.userName; + this.cookie = cacheCookie.cookie; + } + if (!this.cookie) throw "京东 CK 获取失败"; + this.userName = decodeURI(this.userName); + this.CACHE_KEY = `cache_${_md5}_` + this.userName; + this.settings.CACHE_KEY = this.CACHE_KEY; + this.saveSettings(false); + return true; + } catch (e) { + this.notify("错误提示", e); + return false; + } + } + + async render() { + await this.getCookie(); + if (!this.cookie || !this.userName) { + this.notify(this.name, 'cookie或用户名未设置'); + return; + }; + await this.init(); + await this.getPackageData(); + if (this.funcSetting.showBaitiao === '打开') await this.getBaitiaoData(); + if (this.funcSetting.showFruit === '打开') await this.getFruitData(); + if (this.funcSetting.logable === '打开') console.log(this.rangeTimer); + const widget = new ListWidget(); + const padding = 14 * this.basicSetting.scale; + widget.setPadding(padding, padding, padding, padding); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +await Runing(Widget, '', false); \ No newline at end of file diff --git a/Scripts/JD-all-one.js b/Scripts/JD-all-one.js deleted file mode 100644 index 14f3c8c..0000000 --- a/Scripts/JD-all-one.js +++ /dev/null @@ -1,918 +0,0 @@ -// Author: 脑瓜 -// 电报群: https://t.me/Scriptable_JS @anker1209 -// 京豆收支采用2Ya美女的脚本 https://github.com/dompling/Scriptable/tree/master/Scripts -let widgetParam = args.widgetParameter; -const files = FileManager.local(); -// ######################设置#################### -let cookie = ''; // 两个引号之间输入京东cookie,只需填入pt_key和pt_pin字段即可 -let userID = decodeURIComponent( - cookie.match(/pt_pin=(.+?);/) && cookie.match(/pt_pin=(.+?);/)[1], -); -const size = { - logo: 30, // Logo大小 - userImage: 70, // 用户头像大小 - userStack: 103, // 左侧用户信息栏整体宽度 - division: 25, // 左侧与右侧间距 - chartHeight: 120, //京豆K线图高度 -}; -const showBaitiao = false; // 是否显示白条还款信息 -const showPackage = false; // 是否显示包裹信息 - -// ############################################## -const w = new ListWidget(); -w.setPadding(14, 14, 14, 14); - -let isSign; -let CACHE_KEY = 'cache_jd_' + userID; -let packageData; -let packageNum; -let mainData; - -const logo = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/e1902ff8-02e9-4dbf-99c7-cfc85ef3f1b7.png'; -const JDImg = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/43300bf7-61a2-4bd1-94a1-bf2faa2ed9e8.png'; -const jtImg = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/dacbd8f6-8115-4fd6-aedc-95cce83788a9.png'; -const gbImg = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/3947a83b-7aa6-4a53-be34-8fed610ddb77.png'; -const jdImg = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/7ea91cf8-6dea-477c-ae72-cb4d3f646c34.png'; -const plusImg = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/11fbba4a-4f92-4f7c-8fb3-dcff653fe20c.png'; -const baitiaoImg = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/30c40f5b-7428-46c3-a2c0-d81b2b95ec41.png'; -const carImg = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/3b30040f-e378-4502-bec6-ceddb0841b50.png'; -const plusCircle = - 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/06f78540-a5a4-462e-b8c5-98cb8059efc1.png'; - -let beanCount = 0; -let incomeBean = 0; -let expenseBean = 0; -let todayIncomeBean = 0; -let todayExpenseBean = 0; -let maxDays = 6; -let rangeTimer = {}; -let timerKeys = []; -let latelyDays = []; -// Keychain.remove(CACHE_KEY) -const chartTextColor = Color.dynamic( - new Color('000000', 1), - new Color('ffffff', 1), -); - -let baitiaoData = {}; -let baitiaoTitle; -let baitiaoAmount; -let baitiaoDesc; - -// #####################小组件################### -async function renderSmallWidget() { - const bodyStack = w.addStack(); - bodyStack.layoutVertically(); - if (widgetParam == 2) { - await setUserShow(bodyStack); - } else { - await setHeaderShow(bodyStack); - bodyStack.addSpacer(); - if (widgetParam == 1) { - await setChartShow(bodyStack); - } else { - await setBeanShow(bodyStack, 22, 40); - } - bodyStack.addSpacer(); - if (showBaitiao && baitiaoAmount > 0) { - await setSmallBaitiaoShow(bodyStack); - } else { - await rowCell(bodyStack, jtImg, 16, mainData.jintie, 16, '金贴', 10); - await rowCell( - bodyStack, - gbImg, - 18, - mainData['gangbeng'].toString(), - 16, - '钢镚', - 10, - ); - } - } - return w; -} -// #####################中组件################### -async function renderMediumWidget() { - const bodyStack = w.addStack(); - await setUserShow(bodyStack); - bodyStack.addSpacer(size.division); - const mainStack = bodyStack.addStack(); - mainStack.layoutVertically(); - await setHeaderShow(mainStack, JDImg); - mainStack.addSpacer(); - if (showPackage && packageNum > 0) { - await setPackageShow(mainStack); - mainStack.addSpacer(); - if (showBaitiao && baitiaoAmount > 0) { - await setBaitiaoShow(mainStack); - } else { - await setCoinShow(mainStack); - } - } else { - if (widgetParam == 1) { - await setChartShow(mainStack); // 显示京豆收支图表 - } else { - await setBeanShow(mainStack, 30, 50); // 显示昨天和今天京豆收支 - } - mainStack.addSpacer(); - if (showBaitiao && baitiaoAmount > 0) { - await setBaitiaoShow(mainStack); - } else { - await setCoinShow(mainStack); - } - } - return w; -} -// #####################大组件################### -async function renderLargeWidget() { - const bodyStack = w.addStack(); - bodyStack.size = new Size(0, 150); - bodyStack.addSpacer(); - await setUserShow(bodyStack); - bodyStack.addSpacer(); - w.addSpacer(20); - const text = w.addText('\u6211\u600e\u4e48\u8fd9\u4e48\u597d\u770b'); - w.addSpacer(20); - text.font = Font.thinSystemFont(30); - text.centerAlignText(); - const emoji = w.addText('🤣🥰🤪'); - emoji.centerAlignText(); - w.addSpacer(); - return w; -} -// #####################用户信息################### -async function setUserShow(widget) { - const userStack = widget.addStack(); - userStack.size = new Size(size.userStack, 0); - userStack.layoutVertically(); - // 头像 - const userImgStack = userStack.addStack(); - userImgStack.addSpacer(); - const imgStack = userImgStack.addStack(); - imgStack.size = new Size(size.userImage, size.userImage); - imgStack.cornerRadius = size.userImage / 2; - imgStack.backgroundImage = await getImage(userImage); - if (plus) { - const userImg = imgStack.addImage(await getImage(plusImg)); - } - userImgStack.addSpacer(); - userStack.addSpacer(); - // 签到和物流提示 - const tipStack = userStack.addStack(); - tipStack.addSpacer(); - let signIcon; - if (isSign == 0) { - signIcon = SFSymbol.named('checkmark.circle.fill'); - } else { - signIcon = SFSymbol.named('xmark.circle.fill'); - } - const signItem = tipStack.addImage(signIcon.image); - signItem.imageSize = new Size(14, 14); - if (packageNum > 0) { - tipStack.addSpacer(3); - const packageIcon = SFSymbol.named(packageNum + '.circle.fill'); - const packageItem = tipStack.addImage(packageIcon.image); - packageItem.imageSize = new Size(14, 14); - packageItem.tintColor = new Color('FC8600'); - } - // ;[signItem, packageIcon].map(t => t.imageSize = new Size(14,14)) - tipStack.addSpacer(); - userStack.addSpacer(); - // 用户名 - const nameStack = userStack.addStack(); - nameStack.centerAlignContent(); - if (plus) { - const nameImg = nameStack.addImage(await getImage(plusCircle)); - nameImg.imageSize = new Size(16, 16); - } else { - const person = SFSymbol.named('person.circle.fill'); - const nameIcon = nameStack.addImage(person.image); - nameIcon.imageSize = new Size(16, 16); - nameIcon.tintColor = new Color('007aff'); - } - nameStack.addSpacer(5); - const name = nameStack.addText(userName); - name.font = Font.regularSystemFont(14); - userStack.addSpacer(5); - // 京享值 - const valueStack = userStack.addStack(); - valueStack.centerAlignContent(); - const tagIcon = SFSymbol.named('tag.circle.fill'); - const lableIcon = valueStack.addImage(tagIcon.image); - lableIcon.imageSize = new Size(16, 16); - lableIcon.tintColor = new Color('fa2d19'); - valueStack.addSpacer(5); - const value = valueStack.addText(jValue.toString()); - value.font = Font.mediumSystemFont(14); - valueStack.addSpacer(3); - const jStack = valueStack.addStack(); - jStack.backgroundColor = new Color('fa2d19'); - jStack.cornerRadius = 5; - jStack.setPadding(1, 4, 1, 4); - const jLable = jStack.addText('京享'); - jLable.font = Font.systemFont(8); - jLable.textColor = Color.white(); - return widget; -} -// #####################顶部内容################### -async function setHeaderShow(widget, image) { - const topStack = widget.addStack(); - topStack.centerAlignContent(); - const JDLogo = topStack.addImage(await getImage(logo)); - JDLogo.imageSize = new Size(size.logo, size.logo); - if (image) { - topStack.addSpacer(10); - const JD = topStack.addImage(await getImage(image)); - JD.imageSize = new Size(194 * 0.2, 78 * 0.2); - } - topStack.addSpacer(); - const jdBean = topStack.addText(beanCount.toString()); - jdBean.font = Font.boldSystemFont(21); - jdBean.textColor = new Color('fa2d19'); - const desStack = topStack.addStack(); - desStack.layoutVertically(); - desStack.addSpacer(5.5); - const desText = desStack.addText(' 京豆'); - desText.font = Font.mediumSystemFont(10); - desText.textColor = jdBean.textColor; - return widget; -} -// #####################京豆收支################### -async function setBeanShow(widget, textSize, imageSize) { - const beanStack = widget.addStack(); - // 今日收支 - const todayStack = beanStack.addStack(); - todayStack.layoutVertically(); - await rowBeanCell( - todayStack, - todayExpenseBean.toString(), - todayIncomeBean.toString(), - textSize, - '今日', - ); - beanStack.addSpacer(); - // 京豆图片 - const ddStack = beanStack.addStack(); - ddStack.layoutVertically(); - const ddImg = ddStack.addImage(await getImage(jdImg)); - ddImg.imageSize = new Size(imageSize, imageSize); - beanStack.addSpacer(); - // 昨日收支 - const yestodayStack = beanStack.addStack(); - yestodayStack.layoutVertically(); - await rowBeanCell( - yestodayStack, - expenseBean.toString(), - incomeBean.toString(), - textSize, - '昨日', - ); - return widget; -} -// #####################京豆图表################### -async function setChartShow(widget) { - let beanNum = [], - beanDate = []; - Object.keys(rangeTimer).forEach(function (day) { - const numValue = rangeTimer[day]; - const arrDay = day.split('-'); - beanDate.push(arrDay[2]); - beanNum.push(numValue); - }); - if (config.widgetFamily == 'small') { - beanDate.splice(0, 2); - beanNum.splice(0, 2); - } - /*const beanNumStack = widget.addStack() - const canva = new DrawContext() - canva.size = new Size(210,10) - canva.opaque = false - canva.respectScreenScale=true - canva.setTextAlignedCenter() - canva.setFont(Font.mediumSystemFont(10)) - for (let i = 0; i < 6; i++) { - canva.drawTextInRect(beanNum[i].toString(), new Rect(0 + i * 210 / 6, 0, 210/6, 10)) - } - beanNumStack.addImage(canva.getImage())*/ - const chartStack = widget.addStack(); - chartStack.addImage(await createChart()); - const beanDateStack = widget.addStack(); - let showDays = config.widgetFamily == 'small' ? 4 : 6; - for (let i = 0; i < showDays; i++) { - beanDateStack.addSpacer(); - let subStack = beanDateStack.addStack(); - let beanDay = beanDateStack.addText(beanDate[i]); - beanDay.font = new Font('ArialMT', 9); - beanDay.textOpacity = 0.8; - beanDateStack.addSpacer(); - } -} -// #####################物流信息################### -async function setPackageShow(widget) { - const packageStack = widget.addStack(); - const detailStack = packageStack.addStack(); - detailStack.layoutVertically(); - const packageTitleStack = detailStack.addStack(); - packageTitleStack.centerAlignContent(); - const carIcon = packageTitleStack.addImage(await getImage(carImg)); - carIcon.imageSize = new Size(18, 18); - packageTitleStack.addSpacer(4); - const packageTitle = packageTitleStack.addText( - packageData.dealLogList[0]['name'], - ); - packageTitle.lineLimit = 1; - packageTitle.font = Font.mediumSystemFont(12); - detailStack.addSpacer(2); - const packageDesc = detailStack.addText( - packageData.dealLogList[0]['wlStateDesc'], - ); - packageDesc.lineLimit = 3; - packageDesc.font = Font.regularSystemFont(12); - detailStack.addSpacer(2); - const packageStateStack = detailStack.addStack(); - const packageTime = packageStateStack.addText( - packageData.dealLogList[0]['createTime'], - ); - packageTime.font = Font.regularSystemFont(9); - packageTime.textOpacity = 0.7; - packageStateStack.addSpacer(); - const packageState = packageStateStack.addText( - packageData.dealLogList[0]['stateName'], - ); - packageState.font = Font.regularSystemFont(9); - packageTime.textOpacity = 0.7; -} -// #####################金贴&钢镚################## -async function setCoinShow(widget) { - const extraData = widget.addStack(); - await rowCell(extraData, jtImg, 18, mainData.jintie, 16, '金贴', 13); - extraData.addSpacer(); - await rowCell( - extraData, - gbImg, - 18, - mainData['gangbeng'].toString(), - 16, - '钢镚', - 13, - ); - return widget; -} -// #####################京东白条################## -async function setBaitiaoShow(widget) { - const baitiaoStack = widget.addStack(); - baitiaoStack.centerAlignContent(); - const baitiaoImage = baitiaoStack.addImage(await getImage(baitiaoImg)); - baitiaoImage.imageSize = new Size(127 * 0.17, 75 * 0.17); - baitiaoStack.addSpacer(5); - const baitiaoText = baitiaoStack.addText(baitiaoTitle); - baitiaoText.font = Font.regularSystemFont(13); - baitiaoStack.addSpacer(); - const baitiaoValue = baitiaoStack.addText(baitiaoAmount); - baitiaoValue.font = Font.mediumSystemFont(15); - baitiaoStack.addSpacer(); - const baitiaoDate = baitiaoStack.addText(baitiaoDesc); - baitiaoDate.font = Font.regularSystemFont(10); - baitiaoDate.textOpacity = 0.7; -} -// ####################小组件白条################## -async function setSmallBaitiaoShow(widget) { - const oneStack = widget.addStack(); - oneStack.centerAlignContent(); - const baitiaoImage = oneStack.addImage(await getImage(baitiaoImg)); - baitiaoImage.imageSize = new Size(127 * 0.17, 75 * 0.17); - oneStack.addSpacer(5); - const baitiaoText = oneStack.addText(baitiaoTitle); - baitiaoText.font = Font.regularSystemFont(13); - oneStack.addSpacer(); - widget.addSpacer(5); - const twoStack = widget.addStack(); - twoStack.centerAlignContent(); - const baitiaoValue = twoStack.addText(baitiaoAmount); - baitiaoValue.font = Font.mediumSystemFont(15); - twoStack.addSpacer(); - const baitiaoDate = twoStack.addText(baitiaoDesc); - baitiaoDate.font = Font.regularSystemFont(10); - baitiaoDate.textOpacity = 0.7; -} - -async function rowCell( - widget, - image, - imageSize, - text, - textSize, - label, - lableSize, -) { - const rowStack = widget.addStack(); - rowStack.centerAlignContent(); - const rowImg = rowStack.addImage(await getImage(image)); - rowImg.imageSize = new Size(imageSize, imageSize); - rowStack.addSpacer(); - const rowNumber = rowStack.addText(text); - rowNumber.font = Font.regularSystemFont(textSize); - rowStack.addSpacer(); - const rowLabel = rowStack.addText(label); - rowLabel.font = Font.systemFont(lableSize); -} - -async function rowBeanCell(widget, min, add, textSize, label) { - const rowOne = widget.addStack(); - const labelText = rowOne.addText(label); - labelText.font = Font.mediumSystemFont(10); - labelText.textOpacity = 0.7; - const rowTwo = widget.addStack(); - const rowNumber = rowTwo.addText(add); - rowNumber.font = Font.lightSystemFont(textSize); - if (min < 0) { - const rowThree = widget.addStack(); - const minText = rowThree.addText(min); - minText.font = Font.mediumSystemFont(10); - minText.textColor = new Color('fa2d19'); - } -} - -async function init() { - try { - if (!cookie) return; - if (Keychain.contains(CACHE_KEY)) { - rangeTimer = JSON.parse(Keychain.get(CACHE_KEY)); - timerKeys = Object.keys(rangeTimer); - latelyDays = Object.keys(rangeTimer); - if (timerKeys.length >= maxDays) { - for (let i = 0; i < timerKeys.length - maxDays; i++) { - delete rangeTimer[timerKeys[i]]; - } - Keychain.set(CACHE_KEY, JSON.stringify(rangeTimer)); - } - rangeTimer[latelyDays[4]] = 0; - rangeTimer[latelyDays[5]] = 0; - timerKeys = [latelyDays[4], latelyDays[5]]; - } else { - rangeTimer = getDay(5); - timerKeys = Object.keys(rangeTimer); - } - await TotalBean(); - await getAmountData(); - } catch (e) { - console.log(e); - } -} - -async function getAmountData() { - let i = 0, - page = 1; - do { - const response = await getJingBeanBalanceDetail(page); - const result = response.code === '0'; - console.log(`第${page}页:${result ? '请求成功' : '请求失败'}`); - if (response.code === '3') { - i = 1; - console.log(response); - } - if (response && result) { - page++; - let detailList = response.jingDetailList; - if (detailList && detailList.length > 0) { - for (let item of detailList) { - const dates = item.date.split(' '); - if (timerKeys.indexOf(dates[0]) > -1) { - const amount = Number(item.amount); - rangeTimer[dates[0]] += amount; - if (latelyDays[4] === dates[0]) { - const yestodayAmount = Number(item.amount); - if (yestodayAmount > 0) incomeBean += yestodayAmount; - if (yestodayAmount < 0) expenseBean += yestodayAmount; - } - if (latelyDays[5] === dates[0]) { - const todayAmount = Number(item.amount); - if (todayAmount > 0) todayIncomeBean += todayAmount; - if (todayAmount < 0) todayExpenseBean += todayAmount; - } - } else { - i = 1; - Keychain.set(CACHE_KEY, JSON.stringify(rangeTimer)); - break; - } - } - } - } - } while (i === 0); -} - -function getDay(dayNumber) { - let data = {}; - let i = dayNumber; - do { - const today = new Date(); - const year = today.getFullYear(); - const targetday_milliseconds = today.getTime() - 1000 * 60 * 60 * 24 * i; - today.setTime(targetday_milliseconds); - let month = today.getMonth() + 1; - month = month >= 10 ? month : `0${month}`; - let day = today.getDate(); - day = day >= 10 ? day : `0${day}`; - data[`${year}-${month}-${day}`] = 0; - i--; - } while (i >= 0); - return data; -} - -async function TotalBean() { - const url = 'https://wq.jd.com/user/info/QueryJDUserInfo?sceneval=2'; - const request = new Request(url); - request.headers = { - cookie: cookie, - Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', - }; - const response = await request.loadJSON(); - if (response.retcode === 0) { - beanCount = response.base.jdNum; - userImage = response.base.headImageUrl; - userName = response.base.nickname; - jValue = response.base.jvalue; - plus = response.isPlusVip; - } else { - console.log('京东服务器返回空数据'); - } - return response; -} - -async function getJingBeanBalanceDetail(page) { - try { - const options = { - url: `https://bean.m.jd.com/beanDetail/detail.json`, - body: `page=${page}`, - headers: { - 'X-Requested-With': `XMLHttpRequest`, - Connection: `keep-alive`, - 'Accept-Encoding': `gzip, deflate, br`, - 'Content-Type': `application/x-www-form-urlencoded; charset=UTF-8`, - Origin: `https://bean.m.jd.com`, - 'User-Agent': `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15`, - Cookie: cookie, - Host: `bean.m.jd.com`, - Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, - 'Accept-Language': `zh-cn`, - Accept: `application/json, text/javascript, */*; q=0.01`, - }, - }; - let params = { ...options, method: 'POST' }; - let request = new Request(params.url); - Object.keys(params).forEach((key) => { - request[key] = params[key]; - }); - return await request.loadJSON(); - } catch (e) { - console.log(e); - } -} - -async function chartConfig(labels = [], datas = [], chartTextSize, topPadding) { - const chartStr = ` - { - 'type': 'bar', - 'data': { - 'labels': ${JSON.stringify(labels)}, // 替换 - 'datasets': [ - { - type: 'line', - backgroundColor: '#ffffff', - borderColor: getGradientFillHelper('vertical', ['#fa2d19', '#fa2d19']), - 'borderWidth': 2, - pointRadius: 6, - 'fill': false, - 'data': ${JSON.stringify(datas)}, // 数据 - }, - ], - }, - 'options': { - plugins: { - datalabels: { - display: true, - align: 'top', - color: '#${chartTextColor.hex}', // 文字颜色 - font: { - family: 'ArialMT', - size: ${chartTextSize} - } - }, - }, - layout: { - padding: { - left: -20, - right: 0, - top: ${topPadding}, - bottom: 0 - } - }, - responsive: true, - maintainAspectRatio: true, - 'legend': { - 'display': false, - }, - scales: { - xAxes: [ - { - gridLines: { - display: false, - color: '#000000', - }, - ticks: { - display: false, - fontColor: '#000000', - fontSize: '20', - }, - }, - ], - yAxes: [ - { - ticks: { - display: false, - beginAtZero: false, - fontColor: '#000000', - }, - gridLines: { - display: false, - color: '#000000', - }, - }, - ], - }, - }, - }`; - return chartStr; -} - -async function createChart() { - let labels = [], - data = []; - Object.keys(rangeTimer).forEach(function (month) { - const value = rangeTimer[month]; - const arrMonth = month.split('-'); - labels.push(`${arrMonth[1]}.${arrMonth[2]}`); - data.push(value); - }); - let chartTextSize = 18; - let topPadding = 20; - if (config.widgetFamily == 'small') { - data.splice(0, 2); - labels.splice(0, 2); - chartTextSize = chartTextSize + 7; - topPadding = topPadding + 10; - } - const chartStr = await chartConfig(labels, data, chartTextSize, topPadding); - console.log(chartStr); - const url = `https://quickchart.io/chart?w=400&h=${ - size.chartHeight - }&f=png&c=${encodeURIComponent(chartStr)}`; - return await getImage(url); -} - -// 获取金贴和钢镚 -async function getMainData(isSign) { - //津贴查询 - const JTUrl = 'https://ms.jr.jd.com/gw/generic/uc/h5/m/mySubsidyBalance'; - const JTReq = new Request(JTUrl); - JTReq.headers = { - cookie: cookie, - Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', - }; - const JTData = await JTReq.loadJSON(); - //钢镚查询 - const GBUrl = 'https://coin.jd.com/m/gb/getBaseInfo.html'; - const GBReq = new Request(GBUrl); - GBReq.headers = { - cookie: cookie, - Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', - }; - const GBData = await GBReq.loadJSON(); - //签到查询,正则 - const substr = 'pt_key=' + cookie.match(/pt_key=(\S*);/)[1] + ';'; - const substr1 = 'pt_pin=' + cookie.match(/pt_pin=(\S*);/)[1] + ';'; - if (isSign == 1) { - const signUrl = 'http://jd.kzddck.cn:3001/?cookie=' + substr + substr1; - const signReq = new Request(signUrl); - console.log('开始签到'); - try { - console.log('正在签到'); - await signReq.load(); - console.log('签到成功'); - let myDate = new Date(); - let ty = myDate.toLocaleDateString(); - Keychain.set('time' + userID, ty); - console.log('time写入成功'); - } catch (e) { - console.log(e); - console.log('可能签到失败了'); - } - } else if (isSign == 0) { - console.log('今日已签到'); - } - const data = { - jintie: JTData.resultData.data['balance'], - gangbeng: GBData.gbBalance, - }; - return data; -} - -async function cache() { - let cache = Keychain.contains('time' + userID); - let myDate = new Date(); - let ty = myDate.toLocaleDateString(); - if (cache == true) { - let caches = Keychain.get('time' + userID); - if (caches == ty) { - var cachedata = 0; - } - } else { - console.log('还没有签到'); - var cachedata = 1; - } - return cachedata; -} - -async function getPackageData() { - var url = - 'https://wq.jd.com/bases/wuliudetail/notify?sceneval=2&sceneval=2&g_login_type=1&callback'; - var req = new Request(url); - req.headers = { - cookie: cookie, - Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', - }; - var data = await req.loadJSON(); - if (data.errCode == 0) { - await cache(); - console.log('cookie正常'); - } else { - console.log('cookie失效'); - var data = { - dealLogList: [ - { - stateName: 'Cookie失效了', - name: 'Cookie失效了', - img: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/', - createTime: 'Cookie失效了', - wlStateDesc: 'Cookie失效了', - }, - ], - }; - } - return data; -} - -async function getBaitiaoData() { - const req = new Request( - 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', - ); - req.method = 'POST'; - req.body = - 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; - req.headers = { - cookie: cookie, - }; - const res = await req.loadJSON(); - return res; -} - -async function getImage(url) { - const req = await new Request(url); - return await req.loadImage(); -} - -if (typeof require === 'undefined') require = importModule; -const { DmYY, Runing } = require('./DmYY'); -class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = '京东豆多合一'; - this.en = 'jd_all_one'; - this.run(); - } - - run = () => { - if (config.runsInApp) { - this.registerAction('账号设置', async () => { - await this.setAlertInput('账号设置', '京东账号 Ck', { - username: '昵称', - cookie: 'Cookie', - }); - }); - this.registerAction('代理缓存', this.actionSettings); - this.registerAction('基础设置', this.setWidgetConfig); - } - cookie = this.settings.cookie ? `${this.settings.cookie};` : cookie; - userID = decodeURIComponent( - cookie.match(/pt_pin=(.+?);/) && cookie.match(/pt_pin=(.+?);/)[1], - ); - }; - - CookiesData = []; - - // 加载京东 Ck 节点列表 - _loadJDCk = async () => { - try { - const CookiesData = await this.getCache('CookiesJD'); - if (CookiesData) { - this.CookiesData = this.transforJSON(CookiesData); - } - const CookieJD = await this.getCache('CookieJD'); - if (CookieJD) { - const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; - const ck1 = { - cookie: CookieJD, - userName, - }; - this.CookiesData.push(ck1); - } - const Cookie2JD = await this.getCache('CookieJD2'); - if (Cookie2JD) { - const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; - const ck2 = { - cookie: Cookie2JD, - userName, - }; - this.CookiesData.push(ck2); - } - return true; - } catch (e) { - console.log(e); - this.CookiesData = []; - return false; - } - }; - - async actionSettings() { - try { - const table = new UITable(); - if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; - // 如果是节点,则先远程获取 - this.settings.cookieData = this.CookiesData; - this.saveSettings(false); - this.CookiesData.map((t, index) => { - const r = new UITableRow(); - r.addText(`parameter:${index} ${t.userName}`); - r.onSelect = (n) => { - this.settings.username = t.userName; - this.settings.cookie = t.cookie; - this.saveSettings(); - }; - table.addRow(r); - }); - let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; - if (this.settings.cookie) { - body += ',或者使用当前选中Ck:' + this.settings.username; - } - this.notify(this.name, body); - table.present(false); - } catch (e) { - this.notify( - this.name, - '', - 'BoxJS 数据读取失败,请点击通知查看教程', - 'https://chavyleung.gitbook.io/boxjs/awesome/videos', - ); - } - } - - async render() { - await this.getWidgetBackgroundImage(w); - isSign = await cache(); - CACHE_KEY = 'cache_jd_' + userID; - packageData = await getPackageData(); - packageNum = packageData.dealLogList.length; - mainData = await getMainData(isSign); - await init(); - if (showBaitiao) { - baitiaoData = await getBaitiaoData(); - baitiaoTitle = baitiaoData['resultData']['data']['bill']['title']; - baitiaoAmount = baitiaoData['resultData']['data']['bill'][ - 'amount' - ].replace(/,/g, ''); - baitiaoDesc = baitiaoData['resultData']['data']['bill'][ - 'buttonName' - ].replace(/最近还款日/g, ''); - } - - if (config.widgetFamily == 'small') { - return await renderSmallWidget(); - } else if (config.widgetFamily == 'large') { - return await renderLargeWidget(); - } else { - return await renderMediumWidget(); - } - } -} - -await Runing(Widget, '', false); - -//version:1.0.0 diff --git a/Scripts/JDDou.js b/Scripts/JDDou.js index cc5b288..af40af9 100644 --- a/Scripts/JDDou.js +++ b/Scripts/JDDou.js @@ -3,15 +3,15 @@ // icon-color: cyan; icon-glyph: yen-sign; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = "京东豆"; - this.en = "JDDou"; + this.name = '京东豆'; + this.en = 'JDDou'; this.JDRun(module.filename, args); } @@ -21,8 +21,8 @@ class Widget extends DmYY { timerKeys = []; JDCookie = { - cookie: "", - userName: "", + cookie: '', + userName: '', }; CookiesData = []; @@ -42,18 +42,18 @@ class Widget extends DmYY { do { const response = await this.getJingBeanBalanceDetail(page); console.log( - `第${page}页:${response.code === "0" ? "请求成功" : "请求失败"}` + `第${page}页:${response.code === '0' ? '请求成功' : '请求失败'}`, ); - if (response.code === "3") { + if (response.code === '3') { i = 1; console.log(response); } - if (response && response.code === "0") { + if (response && response.code === '0') { page++; let detailList = response.jingDetailList; if (detailList && detailList.length > 0) { for (let item of detailList) { - const dates = item.date.split(" "); + const dates = item.date.split(' '); if (this.timerKeys.indexOf(dates[0]) > -1) { if (this.timerKeys[0] === dates[0]) { const amount = Number(item.amount); @@ -91,19 +91,19 @@ class Widget extends DmYY { TotalBean = async () => { const options = { headers: { - Accept: "application/json,text/plain, */*", - "Content-Type": "application/x-www-form-urlencoded", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zh-cn", - Connection: "keep-alive", + Accept: 'application/json,text/plain, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'zh-cn', + Connection: 'keep-alive', Cookie: this.JDCookie.cookie, - Referer: "https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2", - "User-Agent": - "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1", + Referer: 'https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2', + 'User-Agent': + 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', }, }; - const url = "https://wq.jd.com/user/info/QueryJDUserInfo?sceneval=2"; - const request = new Request(url, { method: "POST" }); + const url = 'https://wq.jd.com/user/info/QueryJDUserInfo?sceneval=2'; + const request = new Request(url, { method: 'POST' }); request.body = options.body; request.headers = options.headers; @@ -111,7 +111,7 @@ class Widget extends DmYY { if (response.retcode === 0) { this.beanCount = response.base.jdNum; } else { - console.log("京东服务器返回空数据"); + console.log('京东服务器返回空数据'); } return response; }; @@ -122,16 +122,16 @@ class Widget extends DmYY { url: `https://bean.m.jd.com/beanDetail/detail.json`, body: `page=${page}`, headers: { - "X-Requested-With": `XMLHttpRequest`, + 'X-Requested-With': `XMLHttpRequest`, Connection: `keep-alive`, - "Accept-Encoding": `gzip, deflate, br`, - "Content-Type": `application/x-www-form-urlencoded; charset=UTF-8`, + 'Accept-Encoding': `gzip, deflate, br`, + 'Content-Type': `application/x-www-form-urlencoded; charset=UTF-8`, Origin: `https://bean.m.jd.com`, - "User-Agent": `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15`, + 'User-Agent': `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15`, Cookie: this.JDCookie.cookie, Host: `bean.m.jd.com`, Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, - "Accept-Language": `zh-cn`, + 'Accept-Language': `zh-cn`, Accept: `application/json, text/javascript, */*; q=0.01`, }, }; @@ -142,7 +142,7 @@ class Widget extends DmYY { }; transforJSON = (str) => { - if (typeof str == "string") { + if (typeof str == 'string') { try { return JSON.parse(str); } catch (e) { @@ -150,7 +150,7 @@ class Widget extends DmYY { return []; } } - console.log("It is not a string!"); + console.log('It is not a string!'); }; setContainer = async (container, { icon, text, desc }) => { @@ -159,7 +159,7 @@ class Widget extends DmYY { const viewer = container.addStack(); viewer.size = new Size(90, 25); - const jdD_icon = await this.$request.get(icon, "IMG"); + const jdD_icon = await this.$request.get(icon, 'IMG'); const imageElemView = viewer.addImage(jdD_icon); imageElemView.centerAlignImage(); imageElemView.imageSize = new Size(25, 25); @@ -169,7 +169,7 @@ class Widget extends DmYY { textview.size = new Size(90, 30); const titleTextItem = textview.addText(text); titleTextItem.font = Font.boldSystemFont(22); - titleTextItem.textColor = new Color("#ffef03"); + titleTextItem.textColor = new Color('#ffef03'); titleTextItem.centerAlignText(); const descView = container.addStack(); @@ -185,29 +185,26 @@ class Widget extends DmYY { setWidget = async (widget) => { const body = widget.addStack(); body.centerAlignContent(); - body.url = "https://bean.m.jd.com/"; + body.url = 'https://bean.m.jd.com/'; const letfContainer = body.addStack(); await this.setContainer(letfContainer, { - icon: - "https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png", + icon: 'https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png', text: `${this.beanCount}`, - desc: "当前京豆", + desc: '当前京豆', }); body.addSpacer(); const centerContainer = body.addStack(); await this.setContainer(centerContainer, { - icon: - "https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png", + icon: 'https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png', text: `+${this.incomeBean}`, - desc: "昨日收入", + desc: '昨日收入', }); body.addSpacer(); const rightContainer = body.addStack(); await this.setContainer(rightContainer, { - icon: - "https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png", + icon: 'https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png', text: `${this.expenseBean}`, - desc: "昨日支出", + desc: '昨日支出', }); return widget; }; @@ -217,7 +214,7 @@ class Widget extends DmYY { }; renderLarge = async (w) => { - const text = w.addText("暂不支持"); + const text = w.addText('暂不支持'); text.font = Font.boldSystemFont(20); text.textColor = this.widgetColor; return w; @@ -236,17 +233,17 @@ class Widget extends DmYY { const widget = new ListWidget(); await this.getWidgetBackgroundImage(widget); const header = widget.addStack(); - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { await this.renderJDHeader(header); } else { await this.renderHeader(header, this.logo, this.name); } widget.addSpacer(20); - if (this.widgetFamily === "medium") { + if (this.widgetFamily === 'medium') { widget.url = - "https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean"; + 'https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean'; await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { + } else if (this.widgetFamily === 'large') { await this.renderLarge(widget); } else { await this.renderSmall(widget); @@ -259,10 +256,10 @@ class Widget extends DmYY { await this.renderHeader(header, this.logo, this.name, this.widgetColor); header.addSpacer(); const headerMore = header.addStack(); - headerMore.url = "https://home.m.jd.com/myJd/home.action"; + headerMore.url = 'https://home.m.jd.com/myJd/home.action'; headerMore.setPadding(1, 10, 1, 10); headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); + headerMore.backgroundColor = new Color('#fff', 0.5); const textItem = headerMore.addText(this.JDCookie.userName); textItem.font = Font.boldSystemFont(12); textItem.textColor = this.widgetColor; @@ -274,64 +271,65 @@ class Widget extends DmYY { jdWebView = async () => { const webView = new WebView(); const url = - "https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024"; + 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; await webView.loadURL(url); await webView.present(false); const req = new Request( - "https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew" + 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', ); - req.method = "POST"; + req.method = 'POST'; req.body = 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; await req.loadJSON(); const cookies = req.response.cookies; - const account = { username: "", cookie: "" }; + const account = { username: '', cookie: '' }; const cookie = []; cookies.forEach((item) => { const value = `${item.name}=${item.value}`; - if (item.name === "pt_key") cookie.push(value); - if (item.name === "pt_pin") { + if (item.name === 'pt_key') cookie.push(value); + if (item.name === 'pt_pin') { account.username = item.value; cookie.push(value); } }); - account.cookie = cookie.join("; "); + account.cookie = cookie.join('; '); console.log(account); if (account.cookie) { this.settings = { ...this.settings, ...account }; this.saveSettings(false); console.log(`${this.name}: cookie获取成功,请关闭窗口!`); - this.notify(this.name, "cookie获取成功,请关闭窗口!"); + this.notify(this.name, 'cookie获取成功,请关闭窗口!'); } }; JDRun = (filename, args) => { if (config.runsInApp) { - this.registerAction("基础设置", this.setWidgetConfig); - this.registerAction("账号设置", async () => { - const index = await this.generateAlert("设置账号信息", [ - "网站登录", - "手动输入", + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('账号设置', async () => { + const index = await this.generateAlert('设置账号信息', [ + '网站登录', + '手动输入', ]); if (index === 0) { await this.jdWebView(); } else { - await this.setAlertInput("账号设置", "京东账号 Ck", { - username: "昵称", - cookie: "Cookie", + await this.setAlertInput('账号设置', '京东账号 Ck', { + username: '昵称', + cookie: 'Cookie', }); } }); - this.registerAction("代理缓存", this.actionSettings); + this.registerAction('代理缓存', this.actionSettings); } let _md5 = this.md5(filename + this.en); this.CACHE_KEY = `cache_${_md5}`; this.JDindex = - typeof args.widgetParameter === "string" + typeof args.widgetParameter === 'string' ? parseInt(args.widgetParameter) : false; - this.logo = "https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png"; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png'; try { const cookieData = this.settings.cookieData; if (this.JDindex !== false && cookieData[this.JDindex]) { @@ -340,11 +338,11 @@ class Widget extends DmYY { this.JDCookie.userName = this.settings.username; this.JDCookie.cookie = this.settings.cookie; } - if (!this.JDCookie.cookie) throw "京东 CK 获取失败"; + if (!this.JDCookie.cookie) throw '京东 CK 获取失败'; this.JDCookie.userName = decodeURI(this.JDCookie.userName); return true; } catch (e) { - this.notify("错误提示", e); + this.notify('错误提示', e); return false; } }; @@ -352,11 +350,12 @@ class Widget extends DmYY { // 加载京东 Ck 节点列表 _loadJDCk = async () => { try { - const CookiesData = await this.getCache("CookiesJD"); + const CookiesData = await this.getCache('CookiesJD'); + this.CookiesData = []; if (CookiesData) { this.CookiesData = this.transforJSON(CookiesData); } - const CookieJD = await this.getCache("CookieJD"); + const CookieJD = await this.getCache('CookieJD'); if (CookieJD) { const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; const ck1 = { @@ -365,7 +364,7 @@ class Widget extends DmYY { }; this.CookiesData.push(ck1); } - const Cookie2JD = await this.getCache("CookieJD2"); + const Cookie2JD = await this.getCache('CookieJD2'); if (Cookie2JD) { const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; const ck2 = { @@ -385,7 +384,7 @@ class Widget extends DmYY { async actionSettings() { try { const table = new UITable(); - if (!(await this._loadJDCk())) throw "BoxJS 数据读取失败"; + if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; // 如果是节点,则先远程获取 this.settings.cookieData = this.CookiesData; this.saveSettings(false); @@ -399,23 +398,18 @@ class Widget extends DmYY { }; table.addRow(r); }); - let body = "京东 Ck 缓存成功,根据下标选择相应的 Ck"; + let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; if (this.settings.cookie) { - body += ",或者使用当前选中Ck:" + this.settings.username; + body += ',或者使用当前选中Ck:' + this.settings.username; } this.notify(this.name, body); table.present(false); } catch (e) { - this.notify( - this.name, - "", - "BoxJS 数据读取失败,请点击通知查看教程", - "https://chavyleung.gitbook.io/boxjs/awesome/videos" - ); + console.log(e); } } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/JDDouK.js b/Scripts/JDDouK.js index 2d0f245..44b70bb 100644 --- a/Scripts/JDDouK.js +++ b/Scripts/JDDouK.js @@ -3,15 +3,15 @@ // icon-color: cyan; icon-glyph: chart-line; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = "京东豆收支"; - this.en = "JDDouK"; + this.name = '京东豆收支'; + this.en = 'JDDouK'; this.rangeDay = 5; // 天数范围配置 this.maxDate = parseInt(this.settings.maxDate) || 12; // 显示最大天数 this.forceCache = false; // 重置缓存 @@ -19,13 +19,13 @@ class Widget extends DmYY { } drawContext = new DrawContext(); - widgetFamily = "medium"; + widgetFamily = 'medium'; rangeTimer = {}; timerKeys = []; isRender = false; JDCookie = { - cookie: "", - userName: "", + cookie: '', + userName: '', }; CookiesData = []; beanCount = 0; @@ -35,9 +35,9 @@ class Widget extends DmYY { let template; let path = this.FILE_MGR.documentsDirectory(); const name = `/${this.en}Template`; - path = path + name + ".js"; + path = path + name + '.js'; if (this.FILE_MGR.fileExists(path)) { - template = require("." + name); + template = require('.' + name); } else { template = ` { @@ -121,9 +121,9 @@ const template = \`${template}\`; module.exports = template;`; this.FILE_MGR.writeString(path, content); } - template = template.replaceAll("__COLOR__", `'${color}'`); - template = template.replace("__LABELS__", `${JSON.stringify(labels)}`); - template = template.replace("__DATAS__", `${JSON.stringify(datas)}`); + template = template.replaceAll('__COLOR__', `'${color}'`); + template = template.replace('__LABELS__', `${JSON.stringify(labels)}`); + template = template.replace('__DATAS__', `${JSON.stringify(datas)}`); return template; }; @@ -164,9 +164,9 @@ module.exports = template;`; page = 1; do { const response = await this.getJingBeanBalanceDetail(page); - const result = response.code === "0"; - console.log(`第${page}页:${result ? "请求成功" : "请求失败"}`); - if (response.code === "3") { + const result = response.code === '0'; + console.log(`第${page}页:${result ? '请求成功' : '请求失败'}`); + if (response.code === '3') { i = 1; console.log(response); } @@ -175,7 +175,7 @@ module.exports = template;`; let detailList = response.jingDetailList; if (detailList && detailList.length > 0) { for (let item of detailList) { - const dates = item.date.split(" "); + const dates = item.date.split(' '); if (this.timerKeys.indexOf(dates[0]) > -1) { const amount = Number(item.amount); this.rangeTimer[dates[0]] += amount; @@ -211,20 +211,20 @@ module.exports = template;`; getJingBeanBalanceDetail = async (page) => { try { - const options = { + const options = { url: `https://bean.m.jd.com/beanDetail/detail.json`, body: `page=${page}`, - headers: { - Accept: "application/json,text/plain, */*", - "Content-Type": "application/x-www-form-urlencoded", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zh-cn", - Connection: "keep-alive", - Cookie: this.JDCookie.cookie, - Referer: "https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2", - "User-Agent": - "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1", - }, + headers: { + Accept: 'application/json,text/plain, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'zh-cn', + Connection: 'keep-alive', + Cookie: this.JDCookie.cookie, + Referer: 'https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2', + 'User-Agent': + 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', + }, }; return await this.$request.post(options.url, options); } catch (e) { @@ -237,16 +237,16 @@ module.exports = template;`; data = []; Object.keys(this.rangeTimer).forEach((month) => { const value = this.rangeTimer[month]; - const arrMonth = month.split("-"); + const arrMonth = month.split('-'); labels.push(`${arrMonth[1]}.${arrMonth[2]}`); data.push(value); }); const chartStr = this.chartConfig(labels, data); console.log(chartStr); const url = `https://quickchart.io/chart?w=580&h=190&f=png&c=${encodeURIComponent( - chartStr + chartStr, )}`; - return await this.$request.get(url, "IMG"); + return await this.$request.get(url, 'IMG'); }; renderSmall = async (w) => { @@ -254,7 +254,7 @@ module.exports = template;`; }; renderLarge = async (w) => { - const text = w.addText("暂不支持"); + const text = w.addText('暂不支持'); text.font = Font.boldSystemFont(20); text.textColor = this.widgetColor; }; @@ -268,17 +268,17 @@ module.exports = template;`; const widget = new ListWidget(); await this.getWidgetBackgroundImage(widget); const header = widget.addStack(); - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { await this.renderJDHeader(header); } else { await this.renderHeader(header, this.logo, this.name, this.widgetColor); } widget.addSpacer(10); const stackChart = widget.addStack(); - if (this.widgetFamily === "medium") { + if (this.widgetFamily === 'medium') { const chart = await this.createChart(); stackChart.addImage(chart); - } else if (this.widgetFamily === "large") { + } else if (this.widgetFamily === 'large') { await this.renderLarge(widget); } else { await this.renderSmall(widget); @@ -292,7 +292,7 @@ module.exports = template;`; let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; if (sColor && reg.test(sColor)) { if (sColor.length === 4) { - let sColorNew = "#"; + let sColorNew = '#'; for (let i = 1; i < 4; i += 1) { sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)); } @@ -300,9 +300,9 @@ module.exports = template;`; } let sColorChange = []; for (let i = 1; i < 7; i += 2) { - sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2))); + sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2))); } - return `rgba(${sColorChange.join(",")},${opacity})`; + return `rgba(${sColorChange.join(',')},${opacity})`; } return sColor; } @@ -310,67 +310,68 @@ module.exports = template;`; jdWebView = async () => { const webView = new WebView(); const url = - "https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024"; + 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; await webView.loadURL(url); await webView.present(false); const req = new Request( - "https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew" + 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', ); - req.method = "POST"; + req.method = 'POST'; req.body = 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; await req.loadJSON(); const cookies = req.response.cookies; - const account = { username: "", cookie: "" }; + const account = { username: '', cookie: '' }; const cookie = []; cookies.forEach((item) => { const value = `${item.name}=${item.value}`; - if (item.name === "pt_key") cookie.push(value); - if (item.name === "pt_pin") { + if (item.name === 'pt_key') cookie.push(value); + if (item.name === 'pt_pin') { account.username = item.value; cookie.push(value); } }); - account.cookie = cookie.join("; "); + account.cookie = cookie.join('; '); console.log(account); if (account.cookie) { this.settings = { ...this.settings, ...account }; this.saveSettings(false); console.log(`${this.name}: cookie获取成功,请关闭窗口!`); - this.notify(this.name, "cookie获取成功,请关闭窗口!"); + this.notify(this.name, 'cookie获取成功,请关闭窗口!'); } }; JDRun = (filename, args) => { if (config.runsInApp) { - this.registerAction("显示天数", async () => { - await this.setAlertInput("设置显示天数周期范围", false, { - maxDate: "天数", + this.registerAction('显示天数', async () => { + await this.setAlertInput('设置显示天数周期范围', false, { + maxDate: '天数', }); }); - this.registerAction("基础设置", this.setWidgetConfig); - this.registerAction("账号设置", async () => { - const index = await this.generateAlert("设置账号信息", [ - "网站登录", - "手动输入", + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('账号设置', async () => { + const index = await this.generateAlert('设置账号信息', [ + '网站登录', + '手动输入', ]); if (index === 0) { await this.jdWebView(); } else { - await this.setAlertInput("账号设置", "京东账号 Ck", { - username: "昵称", - cookie: "Cookie", + await this.setAlertInput('账号设置', '京东账号 Ck', { + username: '昵称', + cookie: 'Cookie', }); } }); - this.registerAction("代理缓存", this.actionSettings); + this.registerAction('代理缓存', this.actionSettings); } let _md5 = this.md5(filename + this.en); - this.logo = "https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png"; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png'; this.JDindex = - typeof args.widgetParameter === "string" + typeof args.widgetParameter === 'string' ? parseInt(args.widgetParameter) : false; try { @@ -381,13 +382,13 @@ module.exports = template;`; this.JDCookie.userName = this.settings.username; this.JDCookie.cookie = this.settings.cookie; } - if (!this.JDCookie.cookie) throw "京东 CK 获取失败"; + if (!this.JDCookie.cookie) throw '京东 CK 获取失败'; this.JDCookie.userName = decodeURI(this.JDCookie.userName); this.CACHE_KEY = `cache_${_md5}_` + this.JDCookie.userName; return true; } catch (e) { - this.notify("错误提示", e); + this.notify('错误提示', e); return false; } }; @@ -397,10 +398,10 @@ module.exports = template;`; await this.renderHeader(header, this.logo, this.name, this.widgetColor); header.addSpacer(); const headerMore = header.addStack(); - headerMore.url = "https://home.m.jd.com/myJd/home.action"; + headerMore.url = 'https://home.m.jd.com/myJd/home.action'; headerMore.setPadding(1, 10, 1, 10); headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); + headerMore.backgroundColor = new Color('#fff', 0.5); const textItem = headerMore.addText(this.JDCookie.userName); textItem.font = Font.boldSystemFont(12); textItem.textColor = this.widgetColor; @@ -412,9 +413,10 @@ module.exports = template;`; // 加载京东 Ck 节点列表 _loadJDCk = async () => { try { - const CookiesData = await this.getCache("CookiesJD"); + const CookiesData = await this.getCache('CookiesJD'); + this.CookiesData = []; if (CookiesData) this.CookiesData = this.transforJSON(CookiesData); - const CookieJD = await this.getCache("CookieJD"); + const CookieJD = await this.getCache('CookieJD'); if (CookieJD) { const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; const ck1 = { @@ -423,7 +425,7 @@ module.exports = template;`; }; this.CookiesData.push(ck1); } - const Cookie2JD = await this.getCache("CookieJD2"); + const Cookie2JD = await this.getCache('CookieJD2'); if (Cookie2JD) { const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; const ck2 = { @@ -443,7 +445,7 @@ module.exports = template;`; async actionSettings() { try { const table = new UITable(); - if (!(await this._loadJDCk())) throw "BoxJS 数据读取失败"; + if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; // 如果是节点,则先远程获取 this.settings.cookieData = this.CookiesData; this.saveSettings(false); @@ -457,23 +459,18 @@ module.exports = template;`; }; table.addRow(r); }); - let body = "京东 Ck 缓存成功,根据下标选择相应的 Ck"; + let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; if (this.settings.cookie) { - body += ",或者使用当前选中Ck:" + this.settings.username; + body += ',或者使用当前选中Ck:' + this.settings.username; } this.notify(this.name, body); table.present(false); } catch (e) { - this.notify( - this.name, - "", - "BoxJS 数据读取失败,请点击通知查看教程", - "https://chavyleung.gitbook.io/boxjs/awesome/videos" - ); + console.log(e); } } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/JDWuLiu.js b/Scripts/JDWuLiu.js index 332e825..a5429eb 100644 --- a/Scripts/JDWuLiu.js +++ b/Scripts/JDWuLiu.js @@ -3,21 +3,21 @@ // icon-color: teal; icon-glyph: truck; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = "京东物流"; - this.en = "JDWuLiu"; + this.name = '京东物流'; + this.en = 'JDWuLiu'; this.JDRun(module.filename, args); } JDCookie = { - cookie: "", - userName: "", + cookie: '', + userName: '', }; CookiesData = []; orderList = []; @@ -27,9 +27,9 @@ class Widget extends DmYY { Accept: `*/*`, Connection: `keep-alive`, Host: `wq.jd.com`, - "Accept-Language": "zh-cn", - "Accept-Encoding": "gzip, deflate, br", - "User-Agent": `Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1`, + 'Accept-Language': 'zh-cn', + 'Accept-Encoding': 'gzip, deflate, br', + 'User-Agent': `Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1`, }, }; @@ -44,8 +44,8 @@ class Widget extends DmYY { getOrderList = async () => { const url = - "https://wq.jd.com/bases/orderlist/list?order_type=2&start_page=1&page_size=10"; - const request = new Request(""); + 'https://wq.jd.com/bases/orderlist/list?order_type=2&start_page=1&page_size=10'; + const request = new Request(''); request.url = url; request.headers = { ...this.opts.headers, @@ -56,7 +56,7 @@ class Widget extends DmYY { try { data = response.orderList.filter((item) => { return ( - item.stateInfo.stateCode === "15" || item.stateInfo.stateCode === "9" + item.stateInfo.stateCode === '15' || item.stateInfo.stateCode === '9' ); }); } catch (e) { @@ -81,19 +81,19 @@ class Widget extends DmYY { setListCell = async (cell, data) => { const { productList = [], - orderDetailLink = "", + orderDetailLink = '', progressInfo = {}, stateInfo, } = data; const product = productList[0]; let body = cell.addStack(); body.url = orderDetailLink; - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { const imageView = body.addStack(); imageView.size = new Size(75, 75); imageView.cornerRadius = 5; imageView.url = product.skuLink; - imageView.backgroundImage = await this.$request.get(product.image, "IMG"); + imageView.backgroundImage = await this.$request.get(product.image, 'IMG'); body.addSpacer(10); } @@ -127,17 +127,17 @@ class Widget extends DmYY { setWidget = async (body) => { body.url = - "https://wqs.jd.com/order/orderlist_merge.shtml?sceneval=2&orderType=waitReceipt"; + 'https://wqs.jd.com/order/orderlist_merge.shtml?sceneval=2&orderType=waitReceipt'; const container = body.addStack(); container.layoutVertically(); if (!this.orderList.length) { - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { const bg = await this.$request.get( - "https://gitee.com/scriptableJS/Scriptable/raw/master/JDWuLiu/cart.png", - "IMG" + 'https://gitee.com/scriptableJS/Scriptable/raw/master/JDWuLiu/cart.png', + 'IMG', ); const cartView = container.addStack(); - if (this.widgetFamily === "large") { + if (this.widgetFamily === 'large') { cartView.size = new Size(285, 150); } else { cartView.size = new Size(285, 50); @@ -146,8 +146,8 @@ class Widget extends DmYY { cartView.addImage(bg); } let textItem = container.addStack(); - if (this.widgetFamily !== "small") textItem.size = new Size(300, 20); - textItem.addText("空空如也"); + if (this.widgetFamily !== 'small') textItem.size = new Size(300, 20); + textItem.addText('空空如也'); textItem.textColor = this.widgetColor; textItem.font = Font.boldSystemFont(15); textItem.lineLimit = 1; @@ -155,7 +155,7 @@ class Widget extends DmYY { return body; } for (let index = 0; index < this.orderList.length; index++) { - if (this.widgetFamily !== "large" && index === 1) { + if (this.widgetFamily !== 'large' && index === 1) { return body; } if (index === 3) { @@ -191,15 +191,15 @@ class Widget extends DmYY { const widget = new ListWidget(); await this.getWidgetBackgroundImage(widget); const header = widget.addStack(); - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { await this.renderJDHeader(header); } else { await this.renderHeader(header, this.logo, this.name, this.widgetColor); } widget.addSpacer(20); - if (this.widgetFamily === "medium") { + if (this.widgetFamily === 'medium') { return await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { + } else if (this.widgetFamily === 'large') { return await this.renderLarge(widget); } else { return await this.renderSmall(widget); @@ -211,10 +211,10 @@ class Widget extends DmYY { await this.renderHeader(header, this.logo, this.name, this.widgetColor); header.addSpacer(); const headerMore = header.addStack(); - headerMore.url = "https://home.m.jd.com/myJd/home.action"; + headerMore.url = 'https://home.m.jd.com/myJd/home.action'; headerMore.setPadding(1, 10, 1, 10); headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); + headerMore.backgroundColor = new Color('#fff', 0.5); const textItem = headerMore.addText(this.JDCookie.userName); textItem.font = Font.boldSystemFont(12); textItem.textColor = this.widgetColor; @@ -226,64 +226,65 @@ class Widget extends DmYY { jdWebView = async () => { const webView = new WebView(); const url = - "https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024"; + 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; await webView.loadURL(url); await webView.present(false); const req = new Request( - "https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew" + 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', ); - req.method = "POST"; + req.method = 'POST'; req.body = 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; await req.loadJSON(); const cookies = req.response.cookies; - const account = { username: "", cookie: "" }; + const account = { username: '', cookie: '' }; const cookie = []; cookies.forEach((item) => { const value = `${item.name}=${item.value}`; - if (item.name === "pt_key") cookie.push(value); - if (item.name === "pt_pin") { + if (item.name === 'pt_key') cookie.push(value); + if (item.name === 'pt_pin') { account.username = item.value; cookie.push(value); } }); - account.cookie = cookie.join("; "); + account.cookie = cookie.join('; '); console.log(account); if (account.cookie) { this.settings = { ...this.settings, ...account }; this.saveSettings(false); console.log(`${this.name}: cookie获取成功,请关闭窗口!`); - this.notify(this.name, "cookie获取成功,请关闭窗口!"); + this.notify(this.name, 'cookie获取成功,请关闭窗口!'); } }; JDRun = (filename, args) => { if (config.runsInApp) { - this.registerAction("基础设置", this.setWidgetConfig); - this.registerAction("账号设置", async () => { - const index = await this.generateAlert("设置账号信息", [ - "网站登录", - "手动输入", + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('账号设置', async () => { + const index = await this.generateAlert('设置账号信息', [ + '网站登录', + '手动输入', ]); if (index === 0) { await this.jdWebView(); } else { - await this.setAlertInput("账号设置", "京东账号 Ck", { - username: "昵称", - cookie: "Cookie", + await this.setAlertInput('账号设置', '京东账号 Ck', { + username: '昵称', + cookie: 'Cookie', }); } }); - this.registerAction("代理缓存", this.actionSettings); + this.registerAction('代理缓存', this.actionSettings); } let _md5 = this.md5(filename + this.en); this.CACHE_KEY = `cache_${_md5}`; this.JDindex = - typeof args.widgetParameter === "string" + typeof args.widgetParameter === 'string' ? parseInt(args.widgetParameter) : false; - this.logo = "https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png"; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png'; try { const cookieData = this.settings.cookieData; if (this.JDindex !== false && cookieData[this.JDindex]) { @@ -292,11 +293,11 @@ class Widget extends DmYY { this.JDCookie.userName = this.settings.username; this.JDCookie.cookie = this.settings.cookie; } - if (!this.JDCookie.cookie) throw "京东 CK 获取失败"; + if (!this.JDCookie.cookie) throw '京东 CK 获取失败'; this.JDCookie.userName = decodeURI(this.JDCookie.userName); return true; } catch (e) { - this.notify("错误提示", e); + this.notify('错误提示', e); return false; } }; @@ -304,11 +305,12 @@ class Widget extends DmYY { // 加载京东 Ck 节点列表 _loadJDCk = async () => { try { - const CookiesData = await this.getCache("CookiesJD"); + const CookiesData = await this.getCache('CookiesJD'); + this.CookiesData = []; if (CookiesData) { this.CookiesData = this.transforJSON(CookiesData); } - const CookieJD = await this.getCache("CookieJD"); + const CookieJD = await this.getCache('CookieJD'); if (CookieJD) { const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; const ck1 = { @@ -317,7 +319,7 @@ class Widget extends DmYY { }; this.CookiesData.push(ck1); } - const Cookie2JD = await this.getCache("CookieJD2"); + const Cookie2JD = await this.getCache('CookieJD2'); if (Cookie2JD) { const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; const ck2 = { @@ -337,7 +339,7 @@ class Widget extends DmYY { async actionSettings() { try { const table = new UITable(); - if (!(await this._loadJDCk())) throw "BoxJS 数据读取失败"; + if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; // 如果是节点,则先远程获取 this.settings.cookieData = this.CookiesData; this.saveSettings(false); @@ -351,23 +353,18 @@ class Widget extends DmYY { }; table.addRow(r); }); - let body = "京东 Ck 缓存成功,根据下标选择相应的 Ck"; + let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; if (this.settings.cookie) { - body += ",或者使用当前选中Ck:" + this.settings.username; + body += ',或者使用当前选中Ck:' + this.settings.username; } this.notify(this.name, body); table.present(false); } catch (e) { - this.notify( - this.name, - "", - "BoxJS 数据读取失败,请点击通知查看教程", - "https://chavyleung.gitbook.io/boxjs/awesome/videos" - ); + console.log(e); } } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/Oild.js b/Scripts/Oild.js new file mode 100644 index 0000000..f2303cd --- /dev/null +++ b/Scripts/Oild.js @@ -0,0 +1,429 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: oil; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +const enumConfig = { + 89: '汽油', + 92: '汽油', + 95: '汽油', + 98: '汽油', + 0: '柴油', +}; + +const provinces = [ + '北京', + '天津', + '上海', + '重庆', + '河北', + '山西', + '辽宁', + '吉林', + '黑龙江', + '江苏', + '浙江', + '安徽', + '福建', + '江西', + '山东', + '河南', + '湖北', + '湖南', + '广东', + '海南', + '四川', + '贵州', + '云南', + '陕西', + '甘肃', + '青海', + '台湾', + '内蒙古', + '广西', + '西藏', + '宁夏', + '新疆', + '香港', + '澳门', +]; + +const squareColor = '#8165AC'; +const processColor = [`#7517F8`, `#E323FF`]; +const processBarColor = [`#4da1ff`, `#4dffdf`]; +const processBarBgColor = '#5A5A89'; + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = 'oilWidget'; + this.name = '油价'; + if (config.runsInApp) { + this.registerAction( + '油价设置', + () => { + return this.setAlertInput('油价设置', '设置类型,多个英文逗号分割', { + oilNumber: '92,95,89,0', + }); + }, + { name: 'oilcan', color: '#E64C57' } + ); + + this.registerAction({ + icon: { name: 'location.circle', color: '#8BE39D' }, + type: 'switch', + title: '自动定位', + val: 'location', + }); + + this.registerAction({ + icon: { name: 'mappin.and.ellipse.circle', color: '#5BBFF6' }, + type: 'select', + title: '油价省份', + val: 'province', + placeholder: '地区', + options: provinces, + }); + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + dataSource = { + DIM_ID: '', + DIM_DATE: '', + CITYNAME: '', + V0: 0, + V95: 0, + V92: 0, + V89: 0, + ZDE0: 0, + ZDE92: 0, + ZDE95: 0, + ZDE89: 0, + QE0: 0, + QE92: 0, + QE95: 0, + QE89: 0, + }; + + init = async () => { + try { + this.oilNumber = `${this.settings.oilNumber || '92'}`.split(','); + const oilNumber = []; + this.oilNumber.forEach((item) => { + if (item && ['92', '95', '89', '0'].includes(item)) { + oilNumber.push(item); + } + }); + this.oilNumber = oilNumber; + if (!this.oilNumber.length) + return this.notify(this.name, '请设置油价型号:92,95,89,0'); + } catch (error) { + return console.log('请设置正确的油价'); + } + this.province = this.settings.province; + this.location = this.settings.location; + + if (this.settings.dataSource) { + this.dataSource = this.settings.dataSource[0]; + } else { + await this.cacheData(); + } + this.cacheData(); + }; + + cacheData = async () => { + try { + await this.getOilPrice(); + } catch (e) { + console.log(e); + } + }; + + getOilPrice = async () => { + if (this.location) { + const location = await Location.current(); + const locationText = await Location.reverseGeocode( + location.latitude, + location.longitude + ); + const { administrativeArea = '' } = locationText[0] || {}; + this.province = administrativeArea.replace('省', ''); + this.settings.province = this.province; + this.saveSettings(false); + } + + if (!this.province) return this.notify(this.name, '请设置油价省份!!'); + console.log(`当前省份:${this.province}`); + this.province = `(CITYNAME="${this.province}")`; + const time = Date.now(); + const url = `https://datacenter-web.eastmoney.com/api/data/v1/get?reportName=RPTA_WEB_YJ_JH&columns=ALL&filter=${encodeURIComponent( + this.province + )}&sortColumns=DIM_DATE&sortTypes=-1&pageNumber=1&pageSize=6&source=WEB&_=${time}`; + + const options = { url }; + const response = await this.$request.post(options); + console.log(response); + if (response.result) { + this.dataSource = response.result.data[0]; + this.settings.dataSource = response.result.data; + this.saveSettings(false); + } + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + renderSmall = async (w) => { + const headerStack = w.addStack(); + const dollarImage = SFSymbol.named(`yensign.circle`).image; + headerStack.centerAlignContent(); + const dollarWidgetImg = headerStack.addImage(dollarImage); + dollarWidgetImg.tintColor = new Color('#f5222d'); + dollarWidgetImg.imageSize = new Size(24, 24); + + headerStack.addSpacer(); + + w.addSpacer(); + const topStack = w.addStack(); + const topLStack = topStack.addStack(); + topLStack.layoutVertically(); + topLStack.addSpacer(); + topLStack.bottomAlignContent(); + const oilPrice = (this.dataSource[`V${this.oilNumber[0]}`] || '').toFixed( + 2 + ); + const timer = (this.dataSource.DIM_DATE.split(' ')[0] || '').split('-'); + const oilNumText = topLStack.addText(`${oilPrice}`); + oilNumText.textColor = this.widgetColor; + oilNumText.minimumScaleFactor = 0.6; + oilNumText.font = Font.boldSystemFont(38); + topLStack.addSpacer(); + + const oilStatus = this.dataSource[`ZDE${this.oilNumber[0]}`] > 0; + const oilZdeImage = SFSymbol.named( + oilStatus ? 'arrow.up' : 'arrow.up' + ).image; + topStack.addSpacer(); + const topRStack = topStack.addStack(); + topRStack.addSpacer(); + topRStack.layoutVertically(); + topRStack.bottomAlignContent(); + const zdeStack = topRStack.addStack(); + zdeStack.setPadding(2, 6, 0, 0); + const oilZdeWidgetImg = zdeStack.addImage(oilZdeImage); + oilZdeWidgetImg.tintColor = new Color(oilStatus ? '#f5222d' : '#a0d911'); + oilZdeWidgetImg.imageSize = new Size(16, 16); + + const timerText = topRStack.addText(`${timer[2]}/${timer[1]}`); + timerText.textColor = this.widgetColor; + timerText.font = Font.systemFont(12); + topRStack.addSpacer(); + + w.addSpacer(); + + const bottomStack = w.addStack(); + bottomStack.addSpacer(); + const rightText = bottomStack.addText( + `${this.oilNumber[0]}#${enumConfig[this.oilNumber[0]]}` + ); + oilNumText.textColor = this.widgetColor; + rightText.font = Font.boldSystemFont(18); + rightText.textOpacity = 0.3; + rightText.rightAlignText(); + bottomStack.addSpacer(5); + return w; + }; + + rowData = (w, oilNumber) => { + const oilPrice = (this.dataSource[`V${oilNumber}`] || '').toFixed(2); + const oilZde = (this.dataSource[`ZDE${oilNumber}`] || '').toFixed(2); + const oilType = enumConfig[oilNumber] || ''; + + const colStack = w.addStack(); + + const oilNumberStack = colStack.addStack(); + const colSize = new Size(40, 40); + oilNumberStack.size = colSize; + oilNumberStack.cornerRadius = 8; + oilNumberStack.borderWidth = 4; + oilNumberStack.borderColor = new Color(squareColor); + oilNumberStack.centerAlignContent(); + this.provideText(`${oilNumber}`, oilNumberStack, { + font: 'bold', + size: 26, + color: new Color(squareColor), + }); + + colStack.addSpacer(7); + + const oilInfoStack = colStack.addStack(); + oilInfoStack.size = new Size(65, colSize.height); + oilInfoStack.layoutVertically(); + oilInfoStack.addSpacer(); + this.provideText(`#${oilType}`, oilInfoStack, { + font: 'light', + size: 12, + color: this.widgetColor, + opacity: 0.5, + }); + + oilInfoStack.addSpacer(2); + + this.provideText(`${oilPrice}`, oilInfoStack, { + font: 'medium', + size: 18, + color: this.widgetColor, + }); + oilInfoStack.addSpacer(); + + const processStack = colStack.addStack(); + processStack.centerAlignContent(); + const processVerWidth = 10; + + let maxCount = 0; + const oilHistory = this.settings.dataSource.map((item) => { + const value = + (item[`ZDE${oilNumber}`] / item[`QE${oilNumber}`]).toFixed(2) * 100; + if (maxCount < value) maxCount = value; + return value; + }); + + maxCount = maxCount * 1.5; + + oilHistory.forEach((item) => { + const processItemStack = processStack.addStack(); + processItemStack.size = new Size(processVerWidth, colSize.height); + processItemStack.cornerRadius = processVerWidth / 2; + processItemStack.backgroundColor = new Color(processBarBgColor); + if (item > 0) processItemStack.addSpacer(); + processItemStack.layoutVertically(); + + const itemBarStack = processItemStack.addStack(); + itemBarStack.cornerRadius = processItemStack.cornerRadius; + itemBarStack.size = new Size( + processVerWidth, + colSize.height * (Math.abs(item) / maxCount) + ); + itemBarStack.backgroundGradient = this.gradient(processColor); + if (item < 0) processItemStack.addSpacer(); + processStack.addSpacer(); + }); + + colStack.addSpacer(); + + const oilZdeStack = colStack.addStack(); + + const oilZdeSize = new Size(80, 10); + + oilZdeStack.layoutVertically(); + oilZdeStack.size = new Size(oilZdeSize.width, colSize.height); + oilZdeStack.centerAlignContent(); + + const oilZdeValueStack = oilZdeStack.addStack(); + oilZdeValueStack.centerAlignContent(); + oilZdeValueStack.addSpacer(); + this.provideText(`${oilZde > 0 ? '+' : ''} ${oilZde}`, oilZdeValueStack, { + font: 'light', + size: 14, + color: this.widgetColor, + }); + + const oilZdeImage = SFSymbol.named( + oilZde > 0 ? 'arrow.up' : 'arrow.down' + ).image; + + oilZdeValueStack.addSpacer(10); + + const oilZdeWidgetImg = oilZdeValueStack.addImage(oilZdeImage); + oilZdeWidgetImg.tintColor = new Color(oilZde > 0 ? '#f5222d' : '#a0d911'); + oilZdeWidgetImg.imageSize = new Size(10, 10); + + oilZdeValueStack.addSpacer(); + + oilZdeStack.addSpacer(5); + + const oilZdeValue = Math.abs(parseFloat(oilZde)); + const processBarBgStack = oilZdeStack.addStack(); + + processBarBgStack.cornerRadius = 5; + processBarBgStack.size = oilZdeSize; + processBarBgStack.backgroundColor = new Color(processBarBgColor); + + if (oilZde < 0) processBarBgStack.addSpacer(); + + const processBarStack = processBarBgStack.addStack(); + + const linear = new LinearGradient(); + linear.colors = processBarColor.map((item) => new Color(item)); + linear.locations = [0, 0.5]; + linear.startPoint = new Point(0, 0); + linear.endPoint = new Point(1, 1); + + processBarStack.backgroundGradient = linear; + processBarStack.cornerRadius = oilZdeSize.height / 2; + processBarStack.size = new Size( + parseInt(oilZdeSize.width * oilZdeValue), + oilZdeSize.height + ); + + if (oilZde > 0) processBarBgStack.addSpacer(); + }; + + renderLarge = async (w) => { + return this.notSupport(w); + }; + + renderBorder = (stack) => { + stack.borderWidth = 1; + }; + + gradient = (color, config = { locations: [0, 0.5] }) => { + const linear = new LinearGradient(); + linear.colors = color.map((item) => new Color(item)); + linear.locations = config.locations; + return linear; + }; + + renderMedium = async (w) => { + w.addSpacer(); + this.oilNumber.forEach((oilNumber, index) => { + if (index > 2) return; + this.rowData(w, oilNumber); + w.addSpacer(); + }); + + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/PriceWidgets.js b/Scripts/PriceWidgets.js new file mode 100644 index 0000000..fd60bf6 --- /dev/null +++ b/Scripts/PriceWidgets.js @@ -0,0 +1,332 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-blue; icon-glyph: dollar; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = ' btc'; + this.name = '比特币'; + + if (config.runsInApp) { + this.registerAction({ + icon: { name: 'centsign.circle', color: '#feda31' }, + type: 'input', + title: '比特币种类', + desc: '设置关注种类', + placeholder: 'BTC,ETH,BNB', + val: 'btcType', + }); + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + format = (str) => { + return parseInt(str) >= 10 ? str : `0${str}`; + }; + + endpoint = 'https://api.coingecko.com/api/v3'; + nomicsEndpoint = 'https://api.nomics.com/v1'; + + dataSource = []; + + init = async () => { + if (this.settings.dataSource && !config.runsInApp) { + this.dataSource = this.settings.dataSource; + } else { + await this.cacheData(this.settings.btcType); + } + this.cacheData(this.settings.btcType); + }; + + cacheData = async (params) => { + try { + const ids = await this.transforBtcType(params); + let response = await this.$request.get( + `${this.endpoint}/coins/markets?vs_currency=usd&ids=${ids}`, + 'STRING' + ); + this.dataSource = []; + response = JSON.parse(response); + if (!response.length) response = await this.getAllJson(); + if (ids) { + const idsData = ids.split(','); + idsData.forEach((id) => { + const it = response.find((item) => item.id === id); + if (it && this.dataSource.length < 6) { + this.dataSource.push({ + id: it.id, + name: it.name, + image: it.image, + symbol: it.symbol.toUpperCase(), + current_price: '' + it.current_price, + high_24h: it.high_24h, + low_24h: it.low_24h, + price_change_percentage_24h: it.price_change_percentage_24h, + last_updated: it.last_updated, + }); + } + }); + } else { + response.forEach((it, index) => { + if (index > 5) return; + this.dataSource.push({ + id: it.id, + name: it.name, + image: it.image, + symbol: it.symbol.toUpperCase(), + current_price: '' + it.current_price, + high_24h: it.high_24h, + low_24h: it.low_24h, + price_change_percentage_24h: it.price_change_percentage_24h, + last_updated: it.last_updated, + }); + }); + } + + this.settings.dataSource = this.dataSource; + this.saveSettings(false); + } catch (e) { + console.log(e); + return []; + } + }; + + transforBtcType = async (params) => { + let btcType; + if (params) btcType = params.split(','); + + const btcAll = await this.getAllJson(); + + if (!btcType) + return btcAll + .filter((item, index) => index < 6) + .map((item) => item.id) + .join(','); + + return btcType + .map((item) => { + const result = + btcAll.find((btc) => btc.symbol.toUpperCase() === item) || {}; + return result.id; + }) + .filter((item) => !!item) + .join(','); + }; + + getAllJson = async () => { + const cachePath = this.FILE_MGR.joinPath( + this.FILE_MGR.libraryDirectory(), + `${Script.name()}/datas` + ); + const filename = `${cachePath}/BTC.json`; + if (!this.FILE_MGR.fileExists(cachePath)) + this.FILE_MGR.createDirectory(cachePath, true); + + if (this.FILE_MGR.fileExists(filename)) { + const data = Data.fromFile(`${cachePath}/BTC.json`).toRawString(); + return JSON.parse(data); + } else { + const response = await this.$request.get( + `${this.endpoint}/coins/markets?vs_currency=usd&ids=` + ); + const data = Data.fromString(JSON.stringify(response)); + this.FILE_MGR.write(filename, data); + return response; + } + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + getSmallBg = async (url) => { + const webview = new WebView(); + let js = `const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.onload = () => { + const { width, height } = img + canvas.width = width + canvas.height = height + ctx.globalAlpha = 0.3 + ctx.drawImage( + img, + -width / 2 + 50, + -height / 2 + 50, + width, + height + ) + const uri = canvas.toDataURL() + completion(uri); + }; + img.src = 'data:image/png;base64,${Data.fromPNG(url).toBase64String()}'`; + let image = await webview.evaluateJavaScript(js, true); + image = image.replace(/^data\:image\/\w+;base64,/, ''); + return Image.fromData(Data.fromBase64String(image)); + }; + + renderSmall = async (widget) => { + const market = this.dataSource[0] || {}; + + widget.url = `https://www.coingecko.com/en/coins/${market.id}`; + + const image = await this.renderImage(market.image); + const backgroundImg = await this.getSmallBg(image); + widget.backgroundColor = this.backGroundColor; + widget.backgroundImage = backgroundImg; + widget.setPadding(12, 12, 12, 12); + const coin = widget.addText(market.symbol.toUpperCase()); + coin.font = Font.heavySystemFont(24); + coin.textColor = this.widgetColor; + + coin.rightAlignText(); + const name = widget.addText(market.name); + name.font = Font.systemFont(10); + name.textColor = Color.gray(); + name.rightAlignText(); + widget.addSpacer(); + + const trend = widget.addText( + `${market.price_change_percentage_24h.toFixed(2)}%` + ); + trend.font = Font.semiboldSystemFont(16); + trend.textColor = + market.price_change_percentage_24h >= 0 ? Color.green() : Color.red(); + + trend.rightAlignText(); + const price = widget.addText(`$ ${market.current_price}`); + price.font = Font.boldSystemFont(28); + price.textColor = this.widgetColor; + price.rightAlignText(); + price.lineLimit = 1; + price.minimumScaleFactor = 0.1; + const history = widget.addText( + `H: ${market.high_24h}, L: ${market.low_24h}` + ); + history.font = Font.systemFont(10); + history.textColor = Color.gray(); + history.rightAlignText(); + history.lineLimit = 1; + history.minimumScaleFactor = 0.1; + return widget; + }; + + rowCell = async (rowStack, market) => { + rowStack.url = `https://www.coingecko.com/zh/coins/${market.id}`; + rowStack.layoutHorizontally(); + const image = await this.renderImage(market.image); + const iconImage = rowStack.addImage(image); + iconImage.imageSize = new Size(28, 28); + iconImage.cornerRadius = 14; + + rowStack.addSpacer(10); + + const centerStack = rowStack.addStack(); + centerStack.layoutVertically(); + + const topCenterStack = centerStack.addStack(); + topCenterStack.layoutHorizontally(); + + const titleText = topCenterStack.addText(market.symbol); + titleText.textColor = this.widgetColor; + titleText.font = this.provideFont('semibold', 16); + + topCenterStack.addSpacer(); + + const priceText = topCenterStack.addText(`$ ${market.current_price}`); + priceText.textColor = this.widgetColor; + priceText.font = this.provideFont('semibold', 15); + priceText.rightAlignText(); + + const bottomCenterStack = centerStack.addStack(); + bottomCenterStack.layoutHorizontally(); + + const subText = bottomCenterStack.addText(market.name); + subText.textColor = Color.gray(); + subText.font = this.provideFont('semibold', 10); + + bottomCenterStack.addSpacer(); + + const historyText = bottomCenterStack.addText( + `H: ${market.high_24h}, L: ${market.low_24h}` + ); + historyText.textColor = Color.gray(); + historyText.font = this.provideFont('semibold', 10); + historyText.rightAlignText(); + + rowStack.addSpacer(8); + + const rateStack = rowStack.addStack(); + rateStack.size = new Size(72, 28); + rateStack.centerAlignContent(); + rateStack.cornerRadius = 4; + rateStack.backgroundColor = + market.price_change_percentage_24h >= 0 ? Color.green() : Color.red(); + const rateText = rateStack.addText( + (market.price_change_percentage_24h >= 0 ? '+' : '') + + market.price_change_percentage_24h.toFixed(2) + + '%' + ); + rateText.textColor = new Color('#fff', 0.9); + rateText.font = this.provideFont('semibold', 14); + rateText.minimumScaleFactor = 0.01; + rateText.lineLimit = 1; + }; + + renderLarge = async (widget) => { + widget.setPadding(12, 12, 12, 12); + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + for (let index = 0; index < this.dataSource.length; index++) { + const item = this.dataSource[index]; + const rowCellStack = containerStack.addStack(); + await this.rowCell(rowCellStack, item); + if (index !== this.dataSource.length - 1) containerStack.addSpacer(); + } + return widget; + }; + + renderMedium = async (widget) => { + widget.setPadding(12, 12, 12, 12); + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + for (let index = 0; index < this.dataSource.length; index++) { + if (index > 2) return; + const item = this.dataSource[index]; + const rowCellStack = containerStack.addStack(); + await this.rowCell(rowCellStack, item); + if (index !== 2) containerStack.addSpacer(); + } + return widget; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + if (this.widgetFamily === 'small') await this.renderSmall(widget); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') await this.renderMedium(widget); + if (this.widgetFamily === 'large') await this.renderLarge(widget); + return widget; + } +} + +// @组件代码结束 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/Spotify.js b/Scripts/Spotify.js new file mode 100644 index 0000000..f1446b8 --- /dev/null +++ b/Scripts/Spotify.js @@ -0,0 +1,672 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-blue; icon-glyph: dollar; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); + +const logo = `https://open.spotifycdn.com/cdn/images/favicon.0f31d2ea.ico`; +const fontFamily = "AmericanTypewriter-Bold"; + +const api = { + me: `https://api.spotify.com/v1/me`, + accounts: `https://accounts.spotify.com/api/token`, + currentlyPlayingTrack: `https://api.spotify.com/v1/me/player/currently-playing`, + getTrack: `https://api.spotify.com/v1/tracks/11dFghVXANMlKmJXsNCbNl`, + palyState: `https://api.spotify.com/v1/me/player`, + playlists: `https://api.spotify.com/v1/me/playlists`, + following: `https://api.spotify.com/v1/me/following?type=artist`, +}; + +const scope = [ + "ugc-image-upload", + "playlist-read-collaborative", + "playlist-modify-private", + "playlist-modify-public", + "playlist-read-private", + "user-read-playback-position", + "user-read-recently-played", + "user-top-read", + "user-modify-playback-state", + "user-read-currently-playing", + "user-read-playback-state", + "user-read-private", + "user-read-email", + "user-library-modify", + "user-library-read", + "user-follow-modify", + "user-follow-read", + "streaming", + "app-remote-control", +]; + +const webUri = `https://accounts.spotify.com/zh-CN/login?continue=${encodeURIComponent( + "https://developer.spotify.com/documentation/web-api/reference/get-current-users-profile" +)}`; + +function generateRandomString(length) { + let text = ""; + let possible = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for (let i = 0; i < length; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} + +function convertMillisecondsToHMS(milliseconds) { + // 计算总共有多少秒 + let totalSeconds = Math.floor(milliseconds / 1000); + + // 计算分钟数 + let minutes = Math.floor(totalSeconds / 60); + + // 剩余的秒数 + let seconds = totalSeconds - minutes * 60; + + if (minutes < 10) { + minutes = "0" + minutes; + } + if (seconds < 10) { + seconds = "0" + seconds; + } + + // 组合时分秒 + return minutes + ":" + seconds; +} + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = "Spotify"; + this.name = "声田音乐"; + this.SETTING_KEY = this.md5(this.en); + this._init(); + this.auth2 = args.queryParameters.code + ? args.queryParameters + : this.settings.auth2; + + this.settings.auth2 = this.auth2; + this.saveSettings(false); + + if (config.runsInApp) { + this.registerAction({ + icon: { + name: "airplayaudio.circle.fill", + color: "#65D46E", + }, + title: "登录", + val: "login", + name: "login", + dismissOnSelect: true, + onClick: () => { + return this.getWebToken(); + }, + }); + + this.registerAction({ + icon: { + name: "music.note", + color: "#65D46E", + }, + title: "客户端 ID", + val: "clientId", + name: "clientId", + type: "input", + dismissOnSelect: true, + }); + + this.registerAction("基础设置", this.setWidgetConfig); + } + } + + baseUri = ""; + accessToken = ""; + dataSource = { + currentlyPlayingTrack: {}, + playlists: {}, + me: {}, + following: {}, + }; + + init = async () => { + await this.getAccessToken(); + if (!this.settings.accessToken) return this.notify(this.name, "请登录!!"); + this.accessToken = `${this.settings.accessToken.token_type} ${this.settings.accessToken.access_token}`; + this.dataSource.playlists = await this.getApiRes(api.playlists); + this.dataSource.following = await this.getApiRes(api.following); + this.dataSource.me = await this.getApiRes(api.me); + this.dataSource.currentlyPlayingTrack = await this.getApiRes( + api.currentlyPlayingTrack + ); + }; + + getAccessToken = async () => { + const { accessToken } = this.settings; + if (accessToken) { + const diffTime = (new Date().getTime() - accessToken.time) / 1000; + if (diffTime > accessToken.expires_in) { + return this.refreshToken(); + } + } else if (this.auth2) { + if (!this.auth2.code) + return this.notify( + this.name, + "请登录!!!", + "https://developer.spotify.com" + ); + const { clientId, codeVerifier } = this.settings; + + const options = { + code: this.auth2.code, + client_id: clientId, + code_verifier: codeVerifier, + grant_type: "authorization_code", + redirect_uri: "scriptable:///run/Spotify?openEditor=true", + }; + + const body = Object.entries(options) + .map((item) => `${item[0]}=${item[1]}`) + .join("&"); + + const response = await this.$request.post(api.accounts, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: body, + }); + + console.log(response); + + if (!response.error) { + this.auth2 = null; + this.settings.auth2 = null; + this.settings.accessToken = { + ...this.settings.accessToken, + ...response, + time: new Date().getTime(), + }; + console.log( + `获取 Token 成功,有效期 ${response.expires_in / (60 * 10)} 分钟` + ); + this.saveSettings(); + } + } + }; + + refreshToken = async () => { + const { accessToken, clientId } = this.settings; + + const options = { + client_id: clientId, + grant_type: "refresh_token", + refresh_token: accessToken.refresh_token, + }; + + const body = Object.entries(options) + .map((item) => `${item[0]}=${item[1]}`) + .join("&"); + const response = await this.$request.post(api.accounts, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: this.accessToken, + }, + body: body, + }); + console.log("token 过期,更新"); + console.log(response); + if (!response.error) { + this.auth2 = null; + this.settings.auth2 = null; + this.settings.accessToken = { + ...this.settings.accessToken, + ...response, + time: new Date().getTime(), + }; + console.log( + `刷新 Token 成功,有效期 ${response.expires_in / (60 * 10)} 分钟` + ); + this.saveSettings(false); + } + }; + + getWebToken = async () => { + const clientId = this.settings.clientId; + if (!clientId) + this.notify( + this.name, + "请到 Spotify 官网申请开发者账号创建 APP", + "https://developer.spotify.com" + ); + + this.settings.codeVerifier = generateRandomString(128); + this.saveSettings(false); + + return WebView.loadURL( + `https://dompling.github.io/Spotify?clientId=${clientId}&codeVerifier=${ + this.settings.codeVerifier + }&scope=${encodeURIComponent(scope.join(" "))}` + ); + }; + + getApiRes = async (uri) => { + try { + const response = await this.$request.get(uri, { + headers: { Authorization: this.accessToken }, + }); + if (!response || response.error) + throw new Error(JSON.stringify(response)); + return response; + } catch (error) { + console.log(error); + return {}; + } + }; + + renderLarge = async (widget) => { + const { currentlyPlayingTrack, playlists, me, following } = this.dataSource; + if (currentlyPlayingTrack.is_playing) { + } else { + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + const logoStack = containerStack.addStack(); + + logoStack.centerAlignContent(); + logoStack.addSpacer(); + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = logoStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + logoImageWidget.centerAlignImage(); + + const meStack = containerStack.addStack(); + meStack.centerAlignContent(); + + const avatarImage = await this.$request.get( + me.images[1]?.url || me.images[0]?.url, + "IMG" + ); + + const avatarStack = meStack.addImage(avatarImage); + avatarStack.imageSize = new Size(64, 64); + avatarStack.cornerRadius = avatarStack.imageSize.height / 2; + + meStack.addSpacer(20); + + const infoStack = meStack.addStack(); + infoStack.layoutVertically(); + this.provideText(me.display_name, infoStack, { + size: 16, + font: fontFamily, + }); + infoStack.addSpacer(20); + + this.provideText(me.email, infoStack, { + size: 12, + font: fontFamily, + }); + + containerStack.addSpacer(20); + + const followStack = containerStack.addStack(); + followStack.centerAlignContent(); + this.provideText(`Following`, followStack, { + size: 16, + font: fontFamily, + }); + + followStack.addSpacer(); + + this.provideText(`${following.artists.total || 0}`, followStack, { + size: 16, + font: fontFamily, + }); + + containerStack.addSpacer(10); + + const followingItemStack = containerStack.addStack(); + followingItemStack.addSpacer(); + const itemSize = new Size(48, 48); + for (let index = 0; index < following.artists.items.length; index++) { + if (index === 4) break; + + const item = following.artists.items[index]; + const itemImage = await this.$request.get(item.images[0].url, "IMG"); + const itemImgStack = followingItemStack.addImage(itemImage); + itemImgStack.imageSize = itemSize; + itemImgStack.cornerRadius = itemSize.height / 2; + followingItemStack.addSpacer(); + } + + containerStack.addSpacer(); + + const playListStack = containerStack.addStack(); + + const listItems = playlists.items.reverse(); + const bottomItems = [ + listItems[0], + listItems[1], + listItems[2], + listItems[3], + ].filter((item) => !!item); + + playListStack.addSpacer(); + + for (const index in bottomItems) { + const item = bottomItems[index]; + const itemImage = await this.$request.get(item.images[0].url, "IMG"); + const itemImageStack = playListStack.addImage(itemImage); + itemImageStack.cornerRadius = 6; + itemImageStack.imageSize = new Size(68, 68); + playListStack.addSpacer(); + } + + containerStack.addSpacer(); + } + return widget; + }; + + renderMedium = async (widget) => { + const { currentlyPlayingTrack, playlists } = this.dataSource; + if (currentlyPlayingTrack.is_playing) { + const containerStack = widget.addStack(); + const leftStack = containerStack.addStack(); + leftStack.centerAlignContent(); + + const thumbImage = await this.$request.get( + currentlyPlayingTrack.item.album.images[0].url, + "IMG" + ); + leftStack.addSpacer(); + leftStack.size = new Size(140, 140); + const thumbImgStack = leftStack.addImage(thumbImage); + thumbImgStack.imageSize = leftStack.size; + thumbImgStack.cornerRadius = 12; + leftStack.addSpacer(); + + containerStack.addSpacer(20); + + const rightStack = containerStack.addStack(); + rightStack.layoutVertically(); + rightStack.addSpacer(); + + const logoStack = rightStack.addStack(); + + logoStack.centerAlignContent(); + logoStack.addSpacer(); + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = logoStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + logoImageWidget.centerAlignImage(); + + rightStack.addSpacer(); + + const nameStack = this.provideText( + currentlyPlayingTrack.item.name, + rightStack, + { size: 16, font: fontFamily } + ); + + nameStack.lineLimit = 1; + nameStack.minimumScaleFactor = 0.5; + + rightStack.addSpacer(); + + const athorStack = this.provideText( + currentlyPlayingTrack.item.artists[0].name, + rightStack, + { size: 12, font: fontFamily } + ); + + athorStack.lineLimit = 1; + athorStack.minimumScaleFactor = 0.5; + + rightStack.addSpacer(); + + const actionsStack = rightStack.addStack(); + + const preImage = SFSymbol.named("backward.end.fill").image; + const playImage = SFSymbol.named("pause.fill").image; + const nextImage = SFSymbol.named("forward.end.fill").image; + + const iconSize = new Size(20, 20); + const preStack = actionsStack.addImage(preImage); + preStack.tintColor = this.widgetColor; + preStack.imageSize = iconSize; + + actionsStack.addSpacer(); + + const playStack = actionsStack.addImage(playImage); + playStack.tintColor = this.widgetColor; + playStack.imageSize = iconSize; + + actionsStack.addSpacer(); + + const nextStack = actionsStack.addImage(nextImage); + nextStack.tintColor = this.widgetColor; + nextStack.imageSize = iconSize; + + rightStack.addSpacer(); + + containerStack.addSpacer(); + } else { + const listItems = playlists.items.reverse(); + const topItem = listItems[0]; + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + const topStack = containerStack.addStack(); + topStack.centerAlignContent(); + + const topImg = await this.$request.get(topItem.images[0].url, "IMG"); + const topImageStack = topStack.addImage(topImg); + topImageStack.cornerRadius = 6; + topImageStack.imageSize = new Size(50, 50); + + topStack.addSpacer(20); + + this.provideText(topItem.name, topStack, { font: fontFamily, size: 16 }); + + topStack.addSpacer(); + + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = topStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + + containerStack.addSpacer(); + + const bottomStack = containerStack.addStack(); + + const bottomItems = [ + listItems[1], + listItems[2], + listItems[3], + listItems[4], + ].filter((item) => !!item); + for (const index in bottomItems) { + const item = bottomItems[index]; + const itemImage = await this.$request.get(item.images[0].url, "IMG"); + const itemImageStack = bottomStack.addImage(itemImage); + itemImageStack.cornerRadius = 6; + itemImageStack.imageSize = new Size(65, 65); + if (index !== bottomItems.length - 1) bottomStack.addSpacer(); + } + } + + return widget; + }; + + renderSmall = async (widget) => { + widget.setPadding(0, 0, 0, 0); + const { currentlyPlayingTrack, playlists } = this.dataSource; + + if (currentlyPlayingTrack.is_playing) { + const iconSize = new Size(20, 20); + + const containerStack = widget.addStack(); + containerStack.setPadding(12, 12, 12, 12); + containerStack.layoutVertically(); + + const thumbImage = await this.$request.get( + currentlyPlayingTrack.item.album.images[0].url, + "IMG" + ); + const opacityImg = await this.shadowImage(thumbImage, "#000", 0.3); + containerStack.backgroundImage = opacityImg; + + const topStack = containerStack.addStack(); + topStack.centerAlignContent(); + + const music = SFSymbol.named("timer.circle.fill").image; + const musicStack = topStack.addImage(music); + musicStack.tintColor = Color.white(); + musicStack.imageSize = new Size(14, 14); + + topStack.addSpacer(10); + + this.provideText( + convertMillisecondsToHMS(currentlyPlayingTrack.item.duration_ms), + topStack, + { size: 12, font: fontFamily, color: Color.white() } + ); + + topStack.addSpacer(); + const logoStack = topStack.addStack(); + logoStack.centerAlignContent(); + + logoStack.addSpacer(); + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = logoStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + logoImageWidget.centerAlignImage(); + + containerStack.addSpacer(); + + const nameStack = this.provideText( + currentlyPlayingTrack.item.name, + containerStack, + { size: 16, font: fontFamily, color: Color.white() } + ); + + nameStack.lineLimit = 2; + containerStack.addSpacer(); + + const athorStack = this.provideText( + currentlyPlayingTrack.item.artists[0].name, + containerStack, + { size: 12, font: fontFamily, color: Color.white() } + ); + + athorStack.lineLimit = 1; + athorStack.minimumScaleFactor = 0.5; + + containerStack.addSpacer(); + + const actionsStack = containerStack.addStack(); + + const preImage = SFSymbol.named("backward.end.fill").image; + const playImage = SFSymbol.named("pause.fill").image; + const nextImage = SFSymbol.named("forward.end.fill").image; + + const preStack = actionsStack.addImage(preImage); + preStack.tintColor = Color.white(); + preStack.imageSize = iconSize; + + actionsStack.addSpacer(); + + const playStack = actionsStack.addImage(playImage); + playStack.tintColor = Color.white(); + playStack.imageSize = iconSize; + + actionsStack.addSpacer(); + + const nextStack = actionsStack.addImage(nextImage); + nextStack.tintColor = Color.white(); + nextStack.imageSize = iconSize; + } else { + const listItems = playlists.items.reverse(); + const topItem = listItems[0]; + + const containerStack = widget.addStack(); + containerStack.setPadding(12, 12, 12, 12); + containerStack.layoutVertically(); + + const thumbImage = await this.$request.get(topItem.images[0].url, "IMG"); + const opacityImg = await this.shadowImage(thumbImage, "#000", 0.3); + containerStack.backgroundImage = opacityImg; + + const topStack = containerStack.addStack(); + topStack.addSpacer(); + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = topStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + + containerStack.addSpacer(); + + this.provideText(topItem.name, containerStack, { + font: fontFamily, + size: 16, + color: Color.white(), + }); + + containerStack.addSpacer(); + + const descStack = this.provideText(topItem.description, containerStack, { + font: fontFamily, + size: 12, + color: Color.white(), + }); + + descStack.lineLimit = 2; + descStack.minimumScaleFactor = 0.5; + + containerStack.addSpacer(); + + const actionsStack = containerStack.addStack(); + actionsStack.addSpacer(); + actionsStack.centerAlignContent(); + + const heartImage = SFSymbol.named("heart.circle.fill").image; + const heartStack = actionsStack.addImage(heartImage); + heartStack.tintColor = Color.white(); + heartStack.imageSize = new Size(20, 20); + + actionsStack.addSpacer(5); + + this.provideText(`${topItem.tracks.total}`, actionsStack, { + size: 14, + font: fontFamily, + color: Color.white(), + }); + } + + return widget; + }; + + renderAccessoryInline = async (widget) => { + return widget; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.useDefaultPadding(); + + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === "small") await this.renderSmall(widget); + if (this.widgetFamily === "medium") await this.renderMedium(widget); + if (this.widgetFamily === "large") await this.renderLarge(widget); + if (this.widgetFamily == "accessoryInline") + await this.renderAccessoryInline(widget); + + return widget; + } +} + +// @组件代码结束 +await Runing(Widget, "", false); //远程开发环境 diff --git a/Scripts/VPNBoardPress.js b/Scripts/VPNBoardPress.js index ade6347..f038939 100644 --- a/Scripts/VPNBoardPress.js +++ b/Scripts/VPNBoardPress.js @@ -257,7 +257,7 @@ completion(response); return await this.$request.get(url, 'IMG'); }; - setContent = async (w, size, viewSize) => { + renderContent = async (w, size, viewSize) => { const rest = this.translateFlow(this.dataSource.restData); const use = this.translateFlow(this.dataSource.usedData); const today = this.translateFlow(this.dataSource.todayUsed); @@ -389,7 +389,7 @@ completion(response); const stackContent = w.addStack(); stackContent.addSpacer(); - await this.setContent(stackContent, {w: 360, h: 360}, new Size(80, 80)); + await this.renderContent(stackContent, {w: 360, h: 360}, new Size(80, 80)); stackContent.addSpacer(); w.addSpacer(); @@ -418,7 +418,7 @@ completion(response); renderMedium = async (w) => { const stackBody = w.addStack(); const stackLeft = stackBody.addStack(); - await this.setContent(stackLeft, {w: 360, h: 360}, new Size(140, 140)); + await this.renderContent(stackLeft, {w: 360, h: 360}, new Size(140, 140)); stackBody.addSpacer(10); const stackRight = stackBody.addStack(); stackRight.layoutVertically(); @@ -482,8 +482,10 @@ completion(response); Run = () => { try { if (config.runsInApp) { - this.registerAction('默认账号', this.actionSettings); - this.registerAction('清除账号', this.deletedVpn); + this.registerAction('默认账号', this.actionSettings, { + name: "text.badge.star", + color: "#a0d911", + }); this.registerAction('新增账号', async () => { const account = await this.setAlertInput( '添加账号', '添加账号数据,添加完成之后请去设置默认账号', { @@ -502,6 +504,13 @@ completion(response); this.settings.dataSource = this.settings.dataSource.filter( item => item); this.saveSettings(); + },{ + name: "text.badge.plus", + color: "#fadb14", + }); + this.registerAction('清除账号', this.deletedVpn, { + name: "text.badge.xmark", + color: "#f5222d", }); this.registerAction('基础设置', this.setWidgetConfig); } diff --git a/Scripts/VPNSubscription.js b/Scripts/VPNSubscription.js index 285525b..fec9982 100644 --- a/Scripts/VPNSubscription.js +++ b/Scripts/VPNSubscription.js @@ -3,39 +3,40 @@ // icon-color: deep-gray; icon-glyph: paper-plane; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === 'undefined') require = importModule; -const {DmYY, Runing} = require('./DmYY'); +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = 'VPNSubscription'; - this.en = 'VPNSubscription'; + this.name = "VPNSubscription"; + this.en = "VPNSubscription"; this.CACHE_KEY = this.md5(`dataSouce_${this.en}`); this.Run(); } useBoxJS = false; - today = ''; - logo = 'https://raw.githubusercontent.com/58xinian/icon/master/glados_animation.gif'; + today = ""; + logo = + "https://raw.githubusercontent.com/58xinian/icon/master/glados_animation.gif"; dataSource = { - restData: '0', - usedData: '0', - totalData: '0', - todayData: '0', + restData: "0", + usedData: "0", + totalData: "0", + todayData: "0", isCheckIn: false, }; account = { - title: '', - url: '', + title: "", + url: "", }; - color1 = ['#ef0a6a', '#b6359c']; - color2 = ['#ff54fa', '#fad126']; - color3 = ['#28cfb3', '#72d7cc']; + color1 = ["#ef0a6a", "#b6359c"]; + color2 = ["#ff54fa", "#fad126"]; + color3 = ["#28cfb3", "#72d7cc"]; chartConfig = (data, color, value) => { console.log(data); @@ -49,7 +50,8 @@ class Widget extends DmYY { "data": [${parseFloat(data[0])}], "borderWidth": 0, "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( - color[0])}), + color[0] + )}), } ] }, @@ -75,7 +77,8 @@ class Widget extends DmYY { "data": [${parseFloat(data[1])}], "borderWidth": 0, "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( - color[1])}), + color[1] + )}), } ] }, @@ -108,7 +111,8 @@ class Widget extends DmYY { "data": [${parseFloat(data[2])}], "borderWidth": 0, "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( - color[2])}), + color[2] + )}), } ] }, @@ -140,7 +144,7 @@ class Widget extends DmYY { console.log(template1); console.log(template2); console.log(template3); - return {template1, template2, template3}; + return { template1, template2, template3 }; }; init = async () => { @@ -162,13 +166,13 @@ class Widget extends DmYY { async getdata(url) { const req = new Request(url); - req.method = 'GET'; + req.method = "GET"; await req.load(); - let resp = req.response.headers['subscription-userinfo']; + let resp = req.response.headers["subscription-userinfo"]; resp = [ - (parseInt(resp.match(/upload=([0-9]+);?/)[1])).toFixed(2), - (parseInt(resp.match(/download=([0-9]+);?/)[1])).toFixed(2), - (parseInt(resp.match(/total=([0-9]+);?/)[1])).toFixed(2), + parseInt(resp.match(/upload=([0-9]+);?/)[1]).toFixed(2), + parseInt(resp.match(/download=([0-9]+);?/)[1]).toFixed(2), + parseInt(resp.match(/total=([0-9]+);?/)[1]).toFixed(2), ]; console.log(resp); return resp; @@ -176,35 +180,36 @@ class Widget extends DmYY { gradient = (color) => { const linear = new LinearGradient(); - linear.colors = color.map(item => new Color(item)); + linear.colors = color.map((item) => new Color(item)); linear.locations = [0, 0.5]; return linear; }; formatFileSize(fileSize) { - if (fileSize < (1024 * 1024)) { + if (fileSize < 1024 * 1024) { let temp = fileSize / 1024; temp = temp.toFixed(2); - return temp + 'KB'; - } else if (fileSize < (1024 * 1024 * 1024)) { + return temp + "KB"; + } else if (fileSize < 1024 * 1024 * 1024) { let temp = fileSize / (1024 * 1024); temp = temp.toFixed(2); - return temp + 'MB'; - } else if (fileSize < (1024 * 1024 * 1024 * 1024)) { + return temp + "MB"; + } else if (fileSize < 1024 * 1024 * 1024 * 1024) { let temp = fileSize / (1024 * 1024 * 1024); temp = temp.toFixed(2); - return temp + 'GB'; + return temp + "GB"; } else { let temp = fileSize / (1024 * 1024 * 1024 * 1024); temp = temp.toFixed(2); - return temp + 'TB'; + return temp + "TB"; } } createChart = async (size, chart) => { - const url = `https://quickchart.io/chart?w=${size.w}&h=${size.h}&f=png&c=${encodeURIComponent( - chart)}`; - return await this.$request.get(url, 'IMG'); + const url = `https://quickchart.io/chart?w=${size.w}&h=${ + size.h + }&f=png&c=${encodeURIComponent(chart)}`; + return await this.$request.get(url, "IMG"); }; setContent = async (w, size, viewSize) => { @@ -212,16 +217,14 @@ class Widget extends DmYY { const use = this.dataSource.usedData; const today = this.dataSource.todayData; const total = this.dataSource.totalData; - const data1 = Math.floor(rest / total * 100); - const data2 = Math.floor(use / total * 100); + const data1 = Math.floor((rest / total) * 100); + const data2 = Math.floor((use / total) * 100); const data3 = Math.floor((today / total) * 100); - const data = [ - data1 || 0, data2 || 0, data3 || 0, - ]; - const {template1, template2, template3} = this.chartConfig( - data, - [this.color1, this.color2, this.color3], - this.formatFileSize(this.dataSource.restData), + const data = [data1 || 0, data2 || 0, data3 || 0]; + const { template1, template2, template3 } = this.chartConfig( + data, + [this.color1, this.color2, this.color3], + this.formatFileSize(this.dataSource.restData) ); const stackContent = w.addStack(); @@ -237,7 +240,6 @@ class Widget extends DmYY { const stackContent3 = stackContent2.addStack(); stackContent3.size = stackSize; stackContent3.backgroundImage = await this.createChart(size, template3); - }; setLabelCell = async (stack, data) => { @@ -250,7 +252,7 @@ class Widget extends DmYY { stackIcon.cornerRadius = 8; if (data.isImg) { try { - const icon = await this.$request.get(data.icon, 'IMG'); + const icon = await this.$request.get(data.icon, "IMG"); stackIcon.addImage(icon); } catch (e) { console.log(e); @@ -262,14 +264,14 @@ class Widget extends DmYY { stackCell.addSpacer(5); const stackTitle = stackCell.addStack(); - const title = {...this.textFormat.title}; + const title = { ...this.textFormat.title }; title.color = this.widgetColor; this.provideText(data.title, stackTitle, title); stackCell.addSpacer(5); const stackValue = stackCell.addStack(); - const value = {...this.textFormat.defaultText}; + const value = { ...this.textFormat.defaultText }; value.color = this.widgetColor; this.provideText(data.value, stackValue, title); @@ -277,7 +279,7 @@ class Widget extends DmYY { }; setFooterCell = (stack, data) => { - const title = {...this.textFormat.title}; + const title = { ...this.textFormat.title }; title.color = this.widgetColor; title.size = 10; @@ -298,7 +300,7 @@ class Widget extends DmYY { this.provideText(data.value, stackText, title); stackText.addSpacer(); - const desc = {...this.textFormat.defaultText}; + const desc = { ...this.textFormat.defaultText }; desc.color = this.widgetColor; desc.size = 8; @@ -315,7 +317,9 @@ class Widget extends DmYY { stackLeft.centerAlignContent(); try { const imgIcon = await this.$request.get( - this.account.icon || this.logo, 'IMG'); + this.account.icon || this.logo, + "IMG" + ); const stackImgItem = stackLeft.addImage(imgIcon); stackImgItem.imageSize = new Size(12, 12); stackImgItem.cornerRadius = 4; @@ -323,7 +327,7 @@ class Widget extends DmYY { } catch (e) { console.log(e); } - const title = {...this.textFormat.title}; + const title = { ...this.textFormat.title }; title.color = this.widgetColor; title.size = 12; this.provideText(this.account.title, stackLeft, title); @@ -331,18 +335,21 @@ class Widget extends DmYY { const stackRight = stackHeader.addStack(); stackRight.centerAlignContent(); - const calendar = SFSymbol.named('waveform.path.badge.minus'); + const calendar = SFSymbol.named("waveform.path.badge.minus"); const imgCalendar = stackRight.addImage(calendar.image); imgCalendar.imageSize = new Size(12, 12); - imgCalendar.tintColor = new Color('#00b800'); + imgCalendar.tintColor = new Color("#00b800"); stackRight.addSpacer(5); this.provideText( - this.formatFileSize(this.dataSource.todayData), stackRight, title); + this.formatFileSize(this.dataSource.todayData), + stackRight, + title + ); w.addSpacer(); const stackContent = w.addStack(); stackContent.addSpacer(); - await this.setContent(stackContent, {w: 360, h: 360}, new Size(80, 80)); + await this.setContent(stackContent, { w: 360, h: 360 }, new Size(80, 80)); stackContent.addSpacer(); w.addSpacer(); @@ -352,7 +359,7 @@ class Widget extends DmYY { const stackFooterLeft = stackFooter.addStack(); this.setFooterCell(stackFooterLeft, { value: this.formatFileSize(this.dataSource.restData), - label: '剩余', + label: "剩余", color: this.color1, }); @@ -361,7 +368,7 @@ class Widget extends DmYY { const stackFooterRight = stackFooter.addStack(); this.setFooterCell(stackFooterRight, { value: this.formatFileSize(this.dataSource.usedData), - label: '累计', + label: "累计", color: this.color2, }); @@ -371,48 +378,38 @@ class Widget extends DmYY { renderMedium = async (w) => { const stackBody = w.addStack(); const stackLeft = stackBody.addStack(); - await this.setContent(stackLeft, {w: 360, h: 360}, new Size(140, 140)); + await this.setContent(stackLeft, { w: 360, h: 360 }, new Size(140, 140)); stackBody.addSpacer(10); const stackRight = stackBody.addStack(); stackRight.layoutVertically(); - await this.setLabelCell( - stackRight, { - icon: this.account.icon || this.logo, - title: this.account.title, - value: ``, - isImg: true, - }); - await this.setLabelCell( - stackRight, - { - icon: this.gradient(this.color3), - title: '上传', - value: this.formatFileSize(this.dataSource.todayData), - }, - ); - await this.setLabelCell( - stackRight, - { - icon: this.gradient(this.color2), - title: '累计', - value: this.formatFileSize(this.dataSource.usedData), - }, - ); + await this.setLabelCell(stackRight, { + icon: this.account.icon || this.logo, + title: this.account.title, + value: ``, + isImg: true, + }); + await this.setLabelCell(stackRight, { + icon: this.gradient(this.color3), + title: "上传", + value: this.formatFileSize(this.dataSource.todayData), + }); + await this.setLabelCell(stackRight, { + icon: this.gradient(this.color2), + title: "累计", + value: this.formatFileSize(this.dataSource.usedData), + }); - await this.setLabelCell( - stackRight, - { - icon: this.gradient(this.color1), - title: '剩余', - value: this.formatFileSize(this.dataSource.restData), - }, - ); + await this.setLabelCell(stackRight, { + icon: this.gradient(this.color1), + title: "剩余", + value: this.formatFileSize(this.dataSource.restData), + }); return w; }; renderLarge = async (w) => { - w.addText('暂不支持'); + w.addText("暂不支持"); return w; }; @@ -424,9 +421,9 @@ class Widget extends DmYY { await this.init(); const widget = new ListWidget(); await this.getWidgetBackgroundImage(widget); - if (this.widgetFamily === 'medium') { + if (this.widgetFamily === "medium") { return await this.renderMedium(widget); - } else if (this.widgetFamily === 'large') { + } else if (this.widgetFamily === "large") { return await this.renderLarge(widget); } else { return await this.renderSmall(widget); @@ -436,33 +433,51 @@ class Widget extends DmYY { Run = () => { try { if (config.runsInApp) { - this.registerAction('默认订阅', this.actionSettings); - this.registerAction('清除订阅', this.deletedVpn); - this.registerAction('新增订阅', async () => { + this.registerAction("默认订阅", this.actionSettings, { + name: "text.badge.star", + color: "#a0d911", + }); + this.registerAction("新增订阅", async () => { const account = await this.setAlertInput( - '添加订阅', '添加订阅数据,添加完成之后请去设置默认订阅', { - title: '机场名', - icon: '图标', - url: '订阅地址', - }, false); + "添加订阅", + "添加订阅数据,添加完成之后请去设置默认订阅", + { + title: "机场名", + icon: "图标", + url: "订阅地址", + }, + false + ); if (!this.settings.dataSource) this.settings.dataSource = []; if (!account) return; if (account.title && account.url) { this.settings.dataSource.push(account); } this.settings.dataSource = this.settings.dataSource.filter( - item => item); + (item) => item + ); this.saveSettings(); + },{ + name: "text.badge.plus", + color: "#fadb14", }); - this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction("清除订阅", this.deletedVpn, { + name: "text.badge.xmark", + color: "#f5222d", + }); + this.registerAction("基础设置", this.setWidgetConfig); } this.account = this.settings.account || this.account; - this.CACHE_KEY += '_' + this.account.title; - const index = typeof args.widgetParameter === 'string' ? parseInt( - args.widgetParameter) : false; - if (this.settings.dataSource && this.settings.dataSource[index] && - index !== - false) { + this.CACHE_KEY += "_" + this.account.title; + const index = + typeof args.widgetParameter === "string" + ? parseInt(args.widgetParameter) + : false; + if ( + this.settings.dataSource && + this.settings.dataSource[index] && + index !== false + ) { this.account = this.settings.dataSource[index]; } } catch (e) { @@ -510,9 +525,8 @@ class Widget extends DmYY { console.log(e); } } - } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, '', false); //远程开发环境 +await Runing(Widget, "", false); //远程开发环境 diff --git a/Scripts/VpnBoard.js b/Scripts/VpnBoard.js index 47401d3..d1b1afa 100644 --- a/Scripts/VpnBoard.js +++ b/Scripts/VpnBoard.js @@ -3,24 +3,24 @@ // icon-color: deep-gray; icon-glyph: paper-plane; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === 'undefined') require = importModule; -const {DmYY, Runing} = require('./DmYY'); +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = 'Vpn'; - this.en = 'vpnBoard'; + this.name = "Vpn"; + this.en = "vpnBoard"; this.CACHE_KEY = this.md5(`dataSouce_${this.en}`); this.Run(); } useBoxJS = false; dataSource = { - restData: '0', - usedData: '0', - todayUsed: '0', + restData: "0", + usedData: "0", + todayUsed: "0", isCheckIn: false, }; @@ -28,9 +28,9 @@ class Widget extends DmYY { const color = `#${this.widgetColor.hex}`; let template; let path = this.FILE_MGR.documentsDirectory(); - path = path + '/VPNBoardTemplate.js'; + path = path + "/VPNBoardTemplate.js"; if (this.FILE_MGR.fileExists(path)) { - template = require('./VPNBoardTemplate'); + template = require("./VPNBoardTemplate"); } else { template = ` { @@ -118,27 +118,27 @@ const template = \`${template}\`; module.exports = template;`; this.FILE_MGR.writeString(path, content); } - template = template.replaceAll('__COLOR__', `'${color}'`); - template = template.replace('__LABELS__', `${JSON.stringify(labels)}`); - template = template.replace('__TEXT__', `${JSON.stringify(text)}`); - template = template.replace('__DATAS__', `${JSON.stringify(datas)}`); + template = template.replaceAll("__COLOR__", `'${color}'`); + template = template.replace("__LABELS__", `${JSON.stringify(labels)}`); + template = template.replace("__TEXT__", `${JSON.stringify(text)}`); + template = template.replace("__DATAS__", `${JSON.stringify(datas)}`); return template; }; account = { - title: '', - url: '', - email: '', - password: '', + title: "", + url: "", + email: "", + password: "", }; range = { - '11.1': {todayUsed: '30M'}, - '11.2': {todayUsed: '130M'}, - '11.3': {todayUsed: '300M'}, - '11.4': {todayUsed: '30M'}, - '11.5': {todayUsed: '2g'}, - '11.6': {todayUsed: '3G'}, + 11.1: { todayUsed: "30M" }, + 11.2: { todayUsed: "130M" }, + 11.3: { todayUsed: "300M" }, + 11.4: { todayUsed: "30M" }, + 11.5: { todayUsed: "2g" }, + 11.6: { todayUsed: "3G" }, }; max = 6; @@ -153,7 +153,7 @@ module.exports = template;`; } const date = new Date(); const format = new DateFormatter(); - format.dateFormat = 'MM.dd'; + format.dateFormat = "MM.dd"; const dateDay = format.string(date); this.range[dateDay] = this.dataSource; const rangeKey = Object.keys(this.range); @@ -173,25 +173,28 @@ module.exports = template;`; const table = { url: this.account.url, body: `email=${encodeURIComponent( - this.account.email)}&passwd=${encodeURIComponent( - this.account.password)}&remember_me=on&rumber-me=week`, + this.account.email + )}&passwd=${encodeURIComponent( + this.account.password + )}&remember_me=on&rumber-me=week`, }; const request = new Request(table.url); request.body = table.body; - request.method = 'POST'; + request.method = "POST"; const data = await request.loadString(); try { if ( - JSON.parse(data).msg.match( - /邮箱不存在|邮箱或者密码错误|Mail or password is incorrect/, - ) + JSON.parse(data).msg.match( + /邮箱不存在|邮箱或者密码错误|Mail or password is incorrect/ + ) ) { - this.notify(this.name, '邮箱或者密码错误'); - console.log('登陆失败'); - this.cookie = request.response.cookies( - item => `${item.name}=${item.value}`).join('; '); + this.notify(this.name, "邮箱或者密码错误"); + console.log("登陆失败"); + this.cookie = request.response + .cookies((item) => `${item.name}=${item.value}`) + .join("; "); } else { - console.log('登陆成功'); + console.log("登陆成功"); } } catch (e) { console.log(e); @@ -201,28 +204,27 @@ module.exports = template;`; async checkin() { const url = this.account.url; let checkinPath = - url.indexOf('auth/login') !== -1 ? 'user/checkin' : 'user/_checkin.php'; + url.indexOf("auth/login") !== -1 ? "user/checkin" : "user/_checkin.php"; const checkinreqest = { - url: url.replace(/(auth|user)\/login(.php)*/g, '') + checkinPath, + url: url.replace(/(auth|user)\/login(.php)*/g, "") + checkinPath, headers: { cookie: this.cookie, }, }; - const data = await this.$request.post(checkinreqest, 'STRING'); + const data = await this.$request.post(checkinreqest, "STRING"); if (data.match(/\"msg\"\:/)) { - console.log('签到成功'); + console.log("签到成功"); this.dataSource.isCheckIn = true; } else { - console.log('签到失败'); + console.log("签到失败"); } } async dataResults() { let url = this.account.url; - const userPath = url.indexOf('auth/login') !== -1 - ? 'user' - : 'user/index.php'; - url = url.replace(/(auth|user)\/login(.php)*/g, '') + userPath; + const userPath = + url.indexOf("auth/login") !== -1 ? "user" : "user/index.php"; + url = url.replace(/(auth|user)\/login(.php)*/g, "") + userPath; const webView = new WebView(); await webView.loadURL(url); const js = ` @@ -239,21 +241,21 @@ if($('.progressbar').length){ completion(response); `; const response = await webView.evaluateJavaScript(js, true); - this.dataSource = {...this.dataSource, ...response}; + this.dataSource = { ...this.dataSource, ...response }; } translateFlow(value) { const unit = [ - {unit: 'T', value: 1024 * 1024}, - {unit: 'G', value: 1024}, - {unit: 'M', value: 1}, - {unit: 'K', value: 1 / 1024}, + { unit: "T", value: 1024 * 1024 }, + { unit: "G", value: 1024 }, + { unit: "M", value: 1 }, + { unit: "K", value: 1 / 1024 }, ]; - const data = {unit: '', value: parseFloat(value)}; - unit.forEach(item => { + const data = { unit: "", value: parseFloat(value) }; + unit.forEach((item) => { if (value.indexOf(item.unit) > -1) { data.unit = item.unit; - data.value = Math.floor((parseFloat(value) * item.value) * 100) / 100; + data.value = Math.floor(parseFloat(value) * item.value * 100) / 100; } }); @@ -261,7 +263,9 @@ completion(response); } createChart = async (size) => { - let labels = [], data = [], text = []; + let labels = [], + data = [], + text = []; const rangeKey = Object.keys(this.range); rangeKey.forEach((key) => { labels.push(key); @@ -270,31 +274,32 @@ completion(response); data.push(valueUnit.value); text.push(value); }); - if (this.widgetSize === 'small') { + if (this.widgetSize === "small") { labels = labels.slice(-3); data = data.slice(-3); } console.log(data); const chart = this.chartConfig(labels, data, text); console.log(chart); - const url = `https://quickchart.io/chart?w=${size.w}&h=${size.h}&f=png&c=${encodeURIComponent( - chart)}`; - return await this.$request.get(url, 'IMG'); + const url = `https://quickchart.io/chart?w=${size.w}&h=${ + size.h + }&f=png&c=${encodeURIComponent(chart)}`; + return await this.$request.get(url, "IMG"); }; createDivider(w) { const drawContext = new DrawContext(); - drawContext.size = new Size(543, this.widgetSize === 'small' ? 4 : 2); + drawContext.size = new Size(543, this.widgetSize === "small" ? 4 : 2); const path = new Path(); path.addLine(new Point(1000, 20)); drawContext.addPath(path); - drawContext.setStrokeColor(new Color('#000', 1)); + drawContext.setStrokeColor(new Color("#000", 1)); drawContext.setLineWidth(2); drawContext.strokePath(); const stackLine = w.addStack(); stackLine.borderWidth = 1; - stackLine.borderColor = new Color('#000', 0.4); + stackLine.borderColor = new Color("#000", 0.4); stackLine.addImage(drawContext.getImage()); w.addSpacer(5); } @@ -304,14 +309,15 @@ completion(response); header.centerAlignContent(); const left = header.addStack(); left.centerAlignContent(); - let icon = 'https://raw.githubusercontent.com/58xinian/icon/master/glados_animation.gif'; + let icon = + "https://raw.githubusercontent.com/58xinian/icon/master/glados_animation.gif"; if (this.account.icon) icon = this.account.icon; const stackIcon = left.addStack(); try { - const imgIcon = await this.$request.get(icon, 'IMG'); + const imgIcon = await this.$request.get(icon, "IMG"); const imgIconItem = stackIcon.addImage(imgIcon); let iconSize = new Size(16, 16); - if (this.widgetSize === 'small') iconSize = new Size(12, 12); + if (this.widgetSize === "small") iconSize = new Size(12, 12); imgIconItem.imageSize = iconSize; imgIconItem.cornerRadius = 4; left.addSpacer(5); @@ -319,7 +325,7 @@ completion(response); console.log(e); } - const vpnName = {...this.textFormat.title}; + const vpnName = { ...this.textFormat.title }; vpnName.size = size; vpnName.color = this.widgetColor; this.provideText(this.account.title, left, vpnName); @@ -328,17 +334,21 @@ completion(response); const right = header.addStack(); right.bottomAlignContent(); - const vpnFlow = {...this.textFormat.title}; - vpnFlow.color = new Color('#26c5bc'); + const vpnFlow = { ...this.textFormat.title }; + vpnFlow.color = new Color("#26c5bc"); vpnFlow.size = size; - this.provideText(this.dataSource.isCheckIn ? '已签到' : '未签到', right, vpnFlow); + this.provideText( + this.dataSource.isCheckIn ? "已签到" : "未签到", + right, + vpnFlow + ); w.addSpacer(); - }; + } setLabel(w, data, size) { const stackLabel = w.addStack(); - const textBattery = {...this.textFormat.battery}; + const textBattery = { ...this.textFormat.battery }; textBattery.size = size.label; textBattery.color = this.widgetColor; this.provideText(data.label, stackLabel, textBattery); @@ -355,20 +365,26 @@ completion(response); footer.centerAlignContent(); this.setLabel( - footer, { - label: '剩余:', - color: '#ff3e3e', - value: this.dataSource.restData, - unit: 'GB', - }, size); + footer, + { + label: "剩余:", + color: "#ff3e3e", + value: this.dataSource.restData, + unit: "GB", + }, + size + ); footer.addSpacer(); this.setLabel( - footer, { - label: '累计:', - color: '#dc9e28', - value: this.dataSource.usedData, - unit: 'GB', - }, size); + footer, + { + label: "累计:", + color: "#dc9e28", + value: this.dataSource.usedData, + unit: "GB", + }, + size + ); } setContent = async (w, size) => { @@ -379,22 +395,22 @@ completion(response); renderSmall = async (w) => { await this.setHeader(w, 10); - await this.setContent(w, {w: 195, h: 85}); + await this.setContent(w, { w: 195, h: 85 }); this.createDivider(w); - this.setFooter(w, {label: 6, value: 8}); + this.setFooter(w, { label: 6, value: 8 }); return w; }; renderMedium = async (w) => { await this.setHeader(w, 16); - await this.setContent(w, {w: 390, h: 85}); + await this.setContent(w, { w: 390, h: 85 }); this.createDivider(w); - this.setFooter(w, {label: 14, value: 18}); + this.setFooter(w, { label: 14, value: 18 }); return w; }; renderLarge = async (w) => { - w.addText('暂不支持'); + w.addText("暂不支持"); return w; }; @@ -406,9 +422,9 @@ completion(response); await this.init(); const widget = new ListWidget(); await this.getWidgetBackgroundImage(widget); - if (this.widgetFamily === 'medium') { + if (this.widgetFamily === "medium") { return await this.renderMedium(widget); - } else if (this.widgetFamily === 'large') { + } else if (this.widgetFamily === "large") { return await this.renderLarge(widget); } else { return await this.renderSmall(widget); @@ -418,36 +434,62 @@ completion(response); Run = () => { try { if (config.runsInApp) { - this.registerAction('默认账号', this.actionSettings); - this.registerAction('清除账号', this.deletedVpn); - this.registerAction('新增账号', async () => { - const account = await this.setAlertInput( - '添加账号', '添加账号数据,添加完成之后请去设置默认账号', { - title: '机场名', - icon: '图标', - url: '登陆地址', - email: '邮箱账号', - password: '密码', - }, false); - if (!this.settings.dataSource) this.settings.dataSource = []; - if (!account) return; - if (account.title && account.url && account.email && - account.password) { - this.settings.dataSource.push(account); + this.registerAction("默认账号", this.actionSettings, { + name: "text.badge.star", + color: "#a0d911", + }); + this.registerAction( + "新增账号", + async () => { + const account = await this.setAlertInput( + "添加账号", + "添加账号数据,添加完成之后请去设置默认账号", + { + title: "机场名", + icon: "图标", + url: "登陆地址", + email: "邮箱账号", + password: "密码", + }, + false + ); + if (!this.settings.dataSource) this.settings.dataSource = []; + if (!account) return; + if ( + account.title && + account.url && + account.email && + account.password + ) { + this.settings.dataSource.push(account); + } + this.settings.dataSource = this.settings.dataSource.filter( + (item) => item + ); + this.saveSettings(); + }, + { + name: "text.badge.plus", + color: "#fadb14", } - this.settings.dataSource = this.settings.dataSource.filter( - item => item); - this.saveSettings(); + ); + this.registerAction("清除账号", this.deletedVpn, { + name: "text.badge.xmark", + color: "#f5222d", }); - this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction("基础设置", this.setWidgetConfig); } this.account = this.settings.account || this.account; - this.CACHE_KEY += '_' + this.account.title; - const index = typeof args.widgetParameter === 'string' ? parseInt( - args.widgetParameter) : false; - if (this.settings.dataSource && this.settings.dataSource[index] && - index !== - false) { + this.CACHE_KEY += "_" + this.account.title; + const index = + typeof args.widgetParameter === "string" + ? parseInt(args.widgetParameter) + : false; + if ( + this.settings.dataSource && + this.settings.dataSource[index] && + index !== false + ) { this.account = this.settings.dataSource[index]; } } catch (e) { @@ -461,7 +503,9 @@ completion(response); const dataSource = this.settings.dataSource || []; dataSource.map((t, index) => { const r = new UITableRow(); - r.addText(`parameter:${index} 机场名:${t.title} 账号:${t.email}`); + r.addText( + `parameter:${index} 机场名:${t.title} 账号:${t.email}` + ); r.onSelect = (n) => { this.settings.account = t; this.notify(t.title, `默认账号设置成功\n账号:${t.email}`); @@ -495,9 +539,8 @@ completion(response); console.log(e); } } - } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, '', false); //远程开发环境 +await Runing(Widget, "", false); //远程开发环境 diff --git a/Scripts/ZXTrains.js b/Scripts/ZXTrains.js index 33238dd..d6d24b8 100644 --- a/Scripts/ZXTrains.js +++ b/Scripts/ZXTrains.js @@ -7,232 +7,240 @@ // 2.自动获取待出行列表:https://raw.githubusercontent.com/dompling/Script/master/ZXTrians/ZXTrains.js 按照脚本内容配置 // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "智行火车票"; - this.en = "ZXTrains"; - this.logo = "https://raw.githubusercontent.com/Orz-3/mini/master/Color/zxhc.png"; - this.cacheName = this.md5(`dataSouce_${this.en}`); - if (config.runsInApp) { - this.registerAction("基础设置", this.setWidgetConfig); - } - } - - dataSource = []; - - init = async () => { - try { - this.dataSource = await this.getTrainsList(); - } catch (e) { - console.log(e); - } - }; - - dateToUnixTimestamp(str) { - const dates = new Date(str.replace(/-/g, "/")); - return parseInt(dates.getTime()); - } - - timeAgo(o) { - var n = new Date().getTime(); - var f = n - o; - var bs = f >= 0 ? "前" : "后"; //判断时间点是在当前时间的 之前 还是 之后 - f = Math.abs(f); - if (f < 6e4) { - return "刚刚"; - } //小于60秒,刚刚 - if (f < 36e5) { - return parseInt(f / 6e4) + "分钟" + bs; - } //小于1小时,按分钟 - if (f < 864e5) { - return parseInt(f / 36e5) + "小时" + bs; - } //小于1天按小时 - if (f < 2592e6) { - return parseInt(f / 864e5) + "天" + bs; - } //小于1个月(30天),按天数 - if (f < 31536e6) { - return parseInt(f / 2592e6) + "个月" + bs; - } //小于1年(365天),按月数 - return parseInt(f / 31536e6) + "年" + bs; //大于365天,按年算 - } - - async getTrainsList() { - try { - const travels = await this.getCache("@ZXTrains.travels"); - console.log(travels); - if (travels) return travels; - } catch (e) { - console.log("未找到火车票缓存:" + e); - this.notify(this.name, "", "BoxJS 数据读取失败,请点击通知查看教程", "https://chavyleung.gitbook.io/boxjs/awesome/videos"); - } - return false; - } - - setWidget = async (body) => { - let isNone = true; - try { - for (const item of this.dataSource) { - let { trainFlights, timeDesc } = item.orders[0]; - const data = trainFlights[0]; - const passengerInfos = data.passengerInfos[0]; - const fromDate = this.dateToUnixTimestamp(data.fromTime); - const toDate = this.dateToUnixTimestamp(data.toTime); - const nowDate = parseInt(new Date().getTime()); - if (fromDate - nowDate < 1000 * 60 * 60 * 24 && nowDate < toDate) { - const header = body.addStack(); - this.name = data.title; - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - body.addSpacer(); - - const container = body.addStack(); - container.url = "suanya://"; - container.layoutVertically(); - const timeView = container.addStack(); - timeView.setPadding(10, 10, 10, 10); - timeView.backgroundColor = new Color("#1890ff"); - timeView.cornerRadius = 5; - - const left = timeView.addStack(); - left.layoutVertically(); - left.addSpacer(); - const leftTimer = left.addText(data.showFromTime); - leftTimer.font = Font.boldSystemFont(16); - leftTimer.textColor = Color.white(); - left.addSpacer(); - const leftDesc = left.addText(data.fromCityName); - leftDesc.font = Font.lightSystemFont(12); - leftDesc.textColor = Color.white(); - left.addSpacer(); - - timeView.addSpacer(); - - const center = timeView.addStack(); - center.addSpacer(); - center.layoutVertically(); - const image = await this.$request.get(data.trafficIcon, "IMG"); - const imageView = center.addImage(image); - imageView.imageSize = new Size(40, 40); - center.addSpacer(); - timeView.addSpacer(); - - const right = timeView.addStack(); - right.layoutVertically(); - right.addSpacer(); - const rightTimer = right.addText(data.showToTime); - rightTimer.font = Font.boldSystemFont(16); - rightTimer.textColor = Color.white(); - right.addSpacer(); - const rightDesc = right.addText(data.toCityName); - rightDesc.font = Font.lightSystemFont(12); - rightDesc.textColor = Color.white(); - right.addSpacer(); - - const footerView = container.addStack(); - footerView.setPadding(10, 10, 10, 10); - timeDesc = nowDate < fromDate ? `距离发车还有${this.timeAgo(fromDate)}` : `距离到达还有${this.timeAgo(toDate)}`; - const footerLeftText = footerView.addText(timeDesc); - footerLeftText.font = Font.boldSystemFont(12); - footerLeftText.textColor = this.widgetColor; - footerLeftText.textOpacity = 0.8; - - footerView.addSpacer(); - const footerRightText = footerView.addText( - `${passengerInfos.seatCategory} ${passengerInfos.carriageNo} ${passengerInfos.seatNo} `, - ); - footerRightText.font = Font.boldSystemFont(12); - footerRightText.textColor = this.widgetColor; - footerRightText.textOpacity = 0.8; - - isNone = false; - break; - } - } - } catch (e) { - console.log(e); - } - if (isNone) await this.renderNone(body); - body.addStack(); - return body; - }; - - renderSmall = async (w) => { - return this.renderLarge(w); - }; - - renderLarge = async (w) => { - const header = w.addStack(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - w.addSpacer(20); - const text = w.addText("暂不支持"); - text.font = Font.boldSystemFont(20); - text.textColor = this.widgetColor; - w.addSpacer(); - return w; - }; - - renderMedium = async (w) => { - return await this.setWidget(w); - }; - - renderNone = async (widget) => { - const header = widget.addStack(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - widget.addSpacer(); - const bodyIcon = await this.$request.get( - "https://images3.c-ctrip.com/ztrip/img/dcx_HUOCHE.png", - "IMG", - ); - - const body = widget.addStack(); - body.url = "suanya://"; - - const container = body.addStack(); - container.layoutVertically(); - const bodyIconView = container.addStack(); - bodyIconView.cornerRadius = 5; - - bodyIconView.addSpacer(); - bodyIconView.backgroundColor = new Color("#1890ff"); - const bodyIconItem = bodyIconView.addImage(bodyIcon); - bodyIconItem.imageSize = new Size(90, 60); - bodyIconView.addSpacer(); - - container.addSpacer(20); - const noneView = container.addStack(); - - noneView.addSpacer(); - const noneText = noneView.addText("暂无未出行行程"); - noneText.font = Font.boldSystemFont(14); - noneText.textColor = this.widgetColor; - noneView.addSpacer(); - - return widget; - }; - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - if (this.widgetFamily === "medium") { - await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - await this.renderLarge(widget); - } else { - await this.renderSmall(widget); - } - return widget; - } + constructor(arg) { + super(arg); + this.name = '智行火车票'; + this.en = 'ZXTrains'; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/zxhc.png'; + this.cacheName = this.md5(`dataSouce_${this.en}`); + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + dataSource = []; + + init = async () => { + try { + this.dataSource = await this.getTrainsList(); + } catch (e) { + console.log(e); + } + }; + + dateToUnixTimestamp(str) { + const dates = new Date(str.replace(/-/g, '/')); + return parseInt(dates.getTime()); + } + + timeAgo(o) { + var n = new Date().getTime(); + var f = n - o; + var bs = f >= 0 ? '前' : '后'; //判断时间点是在当前时间的 之前 还是 之后 + f = Math.abs(f); + if (f < 6e4) { + return '刚刚'; + } //小于60秒,刚刚 + if (f < 36e5) { + return parseInt(f / 6e4) + '分钟' + bs; + } //小于1小时,按分钟 + if (f < 864e5) { + return parseInt(f / 36e5) + '小时' + bs; + } //小于1天按小时 + if (f < 2592e6) { + return parseInt(f / 864e5) + '天' + bs; + } //小于1个月(30天),按天数 + if (f < 31536e6) { + return parseInt(f / 2592e6) + '个月' + bs; + } //小于1年(365天),按月数 + return parseInt(f / 31536e6) + '年' + bs; //大于365天,按年算 + } + + async getTrainsList() { + try { + const travels = await this.getCache('@ZXTrains.travels'); + console.log(travels); + if (travels) return travels; + } catch (e) { + console.log('未找到火车票缓存:' + e); + } + return false; + } + + setWidget = async (body) => { + let isNone = true; + try { + for (const item of this.dataSource) { + let { trainFlights, timeDesc } = item.orders[0]; + const data = trainFlights[0]; + const passengerInfos = data.passengerInfos[0]; + const fromDate = this.dateToUnixTimestamp(data.fromTime); + const toDate = this.dateToUnixTimestamp(data.toTime); + const nowDate = parseInt(new Date().getTime()); + if (fromDate - nowDate < 1000 * 60 * 60 * 24 && nowDate < toDate) { + const header = body.addStack(); + this.name = data.title; + await this.renderHeader( + header, + this.logo, + this.name, + this.widgetColor, + ); + body.addSpacer(); + + const container = body.addStack(); + container.url = 'suanya://'; + container.layoutVertically(); + const timeView = container.addStack(); + timeView.setPadding(10, 10, 10, 10); + timeView.backgroundColor = new Color('#1890ff'); + timeView.cornerRadius = 5; + + const left = timeView.addStack(); + left.layoutVertically(); + left.addSpacer(); + const leftTimer = left.addText(data.showFromTime); + leftTimer.font = Font.boldSystemFont(16); + leftTimer.textColor = Color.white(); + left.addSpacer(); + const leftDesc = left.addText(data.fromCityName); + leftDesc.font = Font.lightSystemFont(12); + leftDesc.textColor = Color.white(); + left.addSpacer(); + + timeView.addSpacer(); + + const center = timeView.addStack(); + center.addSpacer(); + center.layoutVertically(); + const image = await this.$request.get(data.trafficIcon, 'IMG'); + const imageView = center.addImage(image); + imageView.imageSize = new Size(40, 40); + center.addSpacer(); + timeView.addSpacer(); + + const right = timeView.addStack(); + right.layoutVertically(); + right.addSpacer(); + const rightTimer = right.addText(data.showToTime); + rightTimer.font = Font.boldSystemFont(16); + rightTimer.textColor = Color.white(); + right.addSpacer(); + const rightDesc = right.addText(data.toCityName); + rightDesc.font = Font.lightSystemFont(12); + rightDesc.textColor = Color.white(); + right.addSpacer(); + + const footerView = container.addStack(); + footerView.setPadding(10, 10, 10, 10); + timeDesc = + nowDate < fromDate + ? `距离发车还有${this.timeAgo(fromDate)}` + : `距离到达还有${this.timeAgo(toDate)}`; + const footerLeftText = footerView.addText(timeDesc); + footerLeftText.font = Font.boldSystemFont(12); + footerLeftText.textColor = this.widgetColor; + footerLeftText.textOpacity = 0.8; + + footerView.addSpacer(); + const footerRightText = footerView.addText( + `${passengerInfos.seatCategory} ${passengerInfos.carriageNo} ${passengerInfos.seatNo} `, + ); + footerRightText.font = Font.boldSystemFont(12); + footerRightText.textColor = this.widgetColor; + footerRightText.textOpacity = 0.8; + + isNone = false; + break; + } + } + } catch (e) { + console.log(e); + } + if (isNone) await this.renderNone(body); + body.addStack(); + return body; + }; + + renderSmall = async (w) => { + return this.renderLarge(w); + }; + + renderLarge = async (w) => { + const header = w.addStack(); + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + w.addSpacer(20); + const text = w.addText('暂不支持'); + text.font = Font.boldSystemFont(20); + text.textColor = this.widgetColor; + w.addSpacer(); + return w; + }; + + renderMedium = async (w) => { + return await this.setWidget(w); + }; + + renderNone = async (widget) => { + const header = widget.addStack(); + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + widget.addSpacer(); + const bodyIcon = await this.$request.get( + 'https://images3.c-ctrip.com/ztrip/img/dcx_HUOCHE.png', + 'IMG', + ); + + const body = widget.addStack(); + body.url = 'suanya://'; + + const container = body.addStack(); + container.layoutVertically(); + const bodyIconView = container.addStack(); + bodyIconView.cornerRadius = 5; + + bodyIconView.addSpacer(); + bodyIconView.backgroundColor = new Color('#1890ff'); + const bodyIconItem = bodyIconView.addImage(bodyIcon); + bodyIconItem.imageSize = new Size(90, 60); + bodyIconView.addSpacer(); + + container.addSpacer(20); + const noneView = container.addStack(); + + noneView.addSpacer(); + const noneText = noneView.addText('暂无未出行行程'); + noneText.font = Font.boldSystemFont(14); + noneText.textColor = this.widgetColor; + noneView.addSpacer(); + + return widget; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + await this.renderLarge(widget); + } else { + await this.renderSmall(widget); + } + return widget; + } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/birthday.js b/Scripts/birthday.js new file mode 100644 index 0000000..255acf8 --- /dev/null +++ b/Scripts/birthday.js @@ -0,0 +1,694 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: car; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); +let mainTextSize = 13; // 倒数、农历、生日文字大小 + +let widthMode = 110; // 中号组件图片尺寸 + +let heightMode = 100; // 中号组件图片尺寸 + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = "birthday"; + this.name = "破壳日"; + this.LEFT_IMG_KEY = `${this.cacheImage}/avatar`; + + if (config.runsInApp) { + this.registerAction({ + title: "头像设置", + menu: [ + { + icon: { name: "person.badge.plus", color: "#52c41a" }, + type: "img", + title: "头像", + name: "avatar", + val: this.cacheImage, + }, + { + icon: { name: "arrow.left.and.right", color: "#13c2c2" }, + type: "input", + title: "头像宽度", + name: "avatarWidth", + }, + { + icon: { name: "arrow.up.and.down", color: "#1890ff" }, + type: "input", + title: "头像高度", + name: "avatarHeight", + }, + ], + }); + + this.registerAction({ + icon: { name: "a.square", color: "#eb2f96" }, + type: "input", + title: "主文字大小", + name: "mainTextSize", + }); + + this.registerAction({ + icon: { + name: "rectangle.and.pencil.and.ellipsis", + color: "#f5222d", + }, + type: "input", + title: "昵称", + placeholder: "用户昵称", + name: "nickname", + }); + + this.registerAction({ + icon: { + name: "rectangle", + color: "#a68585", + }, + type: "color", + title: "昵称阴影", + placeholder: "昵称阴影", + name: "nicknameShadow", + }); + + this.registerAction({ + icon: { + name: "bubble.left", + color: "#faf61c", + }, + type: "input", + title: "寄语", + name: "bless", + }); + + this.registerAction({ + icon: { + name: "25.square.fill", + color: "#fa541c", + }, + type: "switch", + title: "农历", + name: "nongli", + }); + + this.registerAction({ + icon: { + name: "calendar", + color: "#fa8c16", + }, + type: "date", + title: "破壳日", + name: "birthday", + }); + + this.registerAction({ + icon: { + name: "calendar.badge.clock", + color: "#8016fa", + }, + type: "date", + title: "相识", + name: "eday", + }); + + this.registerAction("基础设置", this.setWidgetConfig); + } + } + + getAge = (beginStr) => { + let tmpBirth = {}; + tmpBirth.year = 0; + tmpBirth.month = 0; + tmpBirth.day = 0; + + if (beginStr == null || beginStr == "") { + return; + } + let startDate = new Date(beginStr.replace(/-/g, "/")); + let today = new Date(); + + let startYear = startDate.getFullYear(); + let endYear = today.getFullYear(); + let startMonth = startDate.getMonth() + 1; + let endMonth = today.getMonth() + 1; + let startDay = startDate.getDate(); + let endDay = today.getDate(); + let allDays = 0; + //准确的每个月天数数组,判断闰年平年。 + let allDayArr = []; + //当月计算 + if (startYear == endYear && startMonth == endMonth) { + tmpBirth.day = endDay - startDay; + return tmpBirth; + } + //正常计算 + for (let i = startYear; i <= endYear; i++) { + let currYear = 365; + let yearMonth = 12; + if ((i % 4 == 0 && i % 100 !== 0) || i % 400 == 0) { + allDays += 366; + currYear = 366; + } + let currMonth = 1; + if (i == startYear) { + currMonth = startMonth; + } + if (i == endYear) { + yearMonth = endMonth; + } + //闰年计算 + for (let m = currMonth; m <= yearMonth; m++) { + let fullDays = 30; + + if (m == 1 || m == 3 || m == 8 || m == 10 || m == 12) { + fullDays = 31; + } else if (m == 2) { + if ((i % 4 == 0 && i % 100 !== 0) || i % 400 == 0) { + fullDays = 29; + } else { + fullDays = 28; + } + } + let dayObj = { + fullDays: fullDays, + currDays: fullDays, + }; + if (m == startMonth && i == startYear) { + dayObj = { + fullDays: fullDays, + currDays: fullDays - startDay, + }; + } else if (m == endMonth && i == endYear) { + dayObj = { + fullDays: fullDays, + currDays: endDay, + }; + } + allDayArr.push(dayObj); + } + } + + if (allDayArr.length == 1) { + tmpBirth.day = allDayArr[0].currDays; + } else if (allDayArr.length == 2) { + var d1 = allDayArr[0].currDays; + var d2 = allDayArr[1].currDays; + //月份天数浮动因子决定准确性 + let cfDay = + allDayArr[0].fullDays > allDayArr[allDayArr.length - 1].fullDays + ? allDayArr[allDayArr.length - 1].fullDays + : allDayArr[0].fullDays; + if (d1 + d2 >= cfDay) { + tmpBirth.day = d1 + d2 - cfDay; + tmpBirth.month += 1; + } else { + tmpBirth.day = d1 + d2; + } + } else { + let d1 = allDayArr[0].currDays; + let d2 = allDayArr[allDayArr.length - 1].currDays; + let sumFullDay = 0; + for (let i = 0; i < allDayArr.length; i++) { + sumFullDay += allDayArr[i].fullDays; + } + //月份天数浮动因子决定准确性 + let cfDay = + allDayArr[0].fullDays > allDayArr[allDayArr.length - 1].fullDays + ? allDayArr[allDayArr.length - 1].fullDays + : allDayArr[0].fullDays; + if (d1 + d2 >= cfDay) { + tmpBirth.day = d1 + d2 - cfDay; + tmpBirth.month += 1; + } else { + tmpBirth.day = d1 + d2; + } + tmpBirth.month += allDayArr.length - 2; + + if (tmpBirth.month >= 12) { + tmpBirth.year += Math.floor(tmpBirth.month / 12); + tmpBirth.month = tmpBirth.month - tmpBirth.year * 12; + } + } + return tmpBirth; + }; + + daysBetween = (d) => { + let now = new Date(); + let date = new Date(d.cYear, d.cMonth - 1, d.cDay); + return parseInt((date.getTime() - now.getTime()) / (24 * 3600 * 1000)); + }; + + getAstroToEmoji = (astro) => { + const data = { + 白羊座: "♈", + 金牛座: "♉", + 双子座: "♊", + 巨蟹座: "♋", + 狮子座: "♌", + 处女座: "♍", + 天秤座: "♎", + 天蝎座: "♏", + 射手座: "♐", + 摩羯座: "♑", + 水瓶座: "♒", + 双鱼座: "♓", + 蛇夫座: "⛎", + }; + return data[astro] || ""; + }; + + getAnimalZodiacToEmoji = (zodiac) => { + const data = { + 鼠: "🐭", + 牛: "🐂", + 虎: "🐯", + 兔: "🐇", + 龙: "🐲", + 蛇: "🐍", + 马: "🐴", + 羊: "🐑", + 猴: "🐵", + 鸡: "🐔", + 狗: "🐶", + 猪: "🐷", + }; + return data[zodiac] || ""; + }; + + verifyTime(date) { + let dateFormat = /^(\d{4})-(\d{1,2})-(\d{1,2})$/; + return dateFormat.test(date); + } + + getEdayNumber = (date) => { + var initDay = date.split("-"); + var obj = { + cYear: parseInt(initDay[0]), + cMonth: parseInt(initDay[1]), + cDay: parseInt(initDay[2]), + }; + return Math.abs(this.daysBetween(obj)); + }; + + ajax = async (opt) => { + const type = opt.nongli ? "lunar" : "solar"; + return ( + await this.$request.post(`https://www.iamwawa.cn/home/nongli/ajax`, { + body: `type=${type}&year=${opt.year}&month=${opt.month}&day=${opt.day}`, + }) + ).data; + }; + + init = async () => { + widthMode = Number(this.settings.avatarWidth) || widthMode; + heightMode = Number(this.settings.avatarHeight) || heightMode; + mainTextSize = Number(this.settings.mainTextSize) || mainTextSize; + + await this.FILE_MGR.fileExistsExtra(this.LEFT_IMG_KEY); + this.defaultData = { + username: this.settings.nickname || "", // 姓名 + time: this.settings.birthday || "2022-12-19", // 生日日期 + nongli: this.settings.nongli === "true" || "", // 农历生日 + eday: this.settings.eday || "2022-12-19", //相识 + bless: this.settings.bless || "", + nicknameShadow: this.settings.nicknameShadow || "#e8e8e8", + isLeapMonth: false, //如果是农历闰月第四个参数赋值true即可 + }; + + const { time, nongli, isLeapMonth, eday } = this.defaultData; + const _data = time.split("-"); + const opt = { + year: parseInt(_data[0]), + month: parseInt(_data[1]), + day: parseInt(_data[2]), + nongli, + isLeapMonth, + }; + + if (this.settings.ajax) { + this.ajax(opt).then((res) => { + this.settings.ajax = res; + this.saveSettings(false); + }); + } else { + this.settings.ajax = await this.ajax(opt); + } + this.saveSettings(false); + const response = this.settings.ajax; + + response.animalEmoji = `${this.getAnimalZodiacToEmoji(response.sx)}`; + response.astro = `${this.getAstroToEmoji(response.xz)}`; + + if (this.verifyTime(eday)) { + response.meetDay = this.getEdayNumber(eday); + } + + this.contentText = { ...response, data: {} }; + + this.contentText.this_year_lunar_solar = + this.contentText.this_year_lunar_solar + .replace("年", "-") + .replace("月", "-") + .replace("日", ""); + this.contentText.next_year_lunar_solar = + this.contentText.next_year_lunar_solar + .replace("年", "-") + .replace("月", "-") + .replace("日", ""); + this.contentText.solar = this.contentText.solar + .replace("年", "-") + .replace("月", "-") + .replace("日", ""); + + const tmpBirth = this.getAge(this.defaultData.time); + let ageYear = tmpBirth.year > 0 ? `${tmpBirth.year}岁` : ""; + let ageMonth = tmpBirth.month > 0 ? `${tmpBirth.month}月` : ""; + let ageDay = tmpBirth.day > 0 ? `${tmpBirth.day}天` : "1天"; + const age = ageYear + ageMonth + ageDay; + const dayIcon = tmpBirth.day + ".circle.fill"; + + this.contentText.data = { + tmpBirth, + ageYear, + ageMonth, + ageDay, + age, + dayIcon, + }; + }; + + rowCell = (widget, { icon, color, title, text, dayImage = false }) => { + const subWidget = widget.addStack(); + subWidget.centerAlignContent(); + + const subImg = subWidget.addImage(SFSymbol.named(icon).image); + subImg.tintColor = new Color(color); + subImg.imageSize = new Size(mainTextSize, mainTextSize); + subWidget.addSpacer(4); + const subTitle = subWidget.addText(title || ""); + subTitle.font = Font.systemFont(mainTextSize); + subTitle.textColor = this.widgetColor; + subWidget.addSpacer(); + const subValue = subWidget.addText(text || ""); + subValue.font = Font.systemFont(mainTextSize); + subValue.textColor = this.widgetColor; + subValue.lineLimit = 1; + + if (dayImage) { + subWidget.addSpacer(2); + let dayIcon = subWidget.addImage(SFSymbol.named(dayImage).image); + dayIcon.imageSize = new Size(mainTextSize + 1, mainTextSize + 1); + dayIcon.tintColor = new Color("#1ab6f8"); + } + }; + + animalImg = (text) => { + const { this_year_lunar_solar, solar } = this.contentText; + + const nextBirthday = { + year: this_year_lunar_solar.split("-")[0], + month: this_year_lunar_solar.split("-")[1], + day: this_year_lunar_solar.split("-")[2], + }; + + const preData = { + year: solar.split("-")[0], + month: solar.split("-")[1], + day: solar.split("-")[2], + }; + + const extraTextColor = "fc8ac3"; //环形进度条中心背景颜色及名字、meetDay颜色 + const ringColor = "fc5ead"; //环形进度条颜色 + const canvSize = 172; + const canvTextSize = 45; + const canvas = new DrawContext(); + const canvWidth = 12; + const canvRadius = 80; + const cbgColor = new Color(ringColor, 0.2); + const cfgColor = new Color(ringColor); + const centerColor = new Color(extraTextColor); + const cfontColor = new Color("ffffff"); + canvas.size = new Size(canvSize, canvSize); + canvas.opaque = false; + canvas.respectScreenScale = true; + + const today = new Date(); + const thenDate = new Date( + `${nextBirthday.year}`, + `${nextBirthday.month}` - 1, + `${nextBirthday.day}` + ); + + const passDate = new Date(preData.year, preData.month - 1, preData.day); + + const gap = today.getTime() - passDate.getTime(); + const gap2 = thenDate.getTime() - passDate.getTime(); + const deg = Math.floor((gap / gap2) * 100 * 3.6); + + let ctr = new Point(canvSize / 2, canvSize / 2); + const bgx = ctr.x - canvRadius; + const bgy = ctr.y - canvRadius; + const bgd = 2 * canvRadius; + const bgr = new Rect(bgx, bgy, bgd, bgd); + + canvas.setFillColor(cfgColor); + canvas.setStrokeColor(cbgColor); + canvas.setLineWidth(canvWidth); + canvas.strokeEllipse(bgr); + + for (let t = 0; t < deg; t++) { + const rect_x = + ctr.x + canvRadius * Math.sin((t * Math.PI) / 180) - canvWidth / 2; + const rect_y = + ctr.y - canvRadius * Math.cos((t * Math.PI) / 180) - canvWidth / 2; + const rect_r = new Rect(rect_x, rect_y, canvWidth, canvWidth); + canvas.fillEllipse(rect_r); + } + + const ringBG = new Rect( + bgx + canvWidth / 2 + 8, + bgy + canvWidth / 2 + 8, + canvRadius * 2 - canvWidth - 16, + canvRadius * 2 - canvWidth - 16 + ); + canvas.setFillColor(centerColor); + canvas.setLineWidth(0); + canvas.fillEllipse(ringBG); + + const canvTextRect = new Rect(0, 70 - canvTextSize / 2, canvSize, 80); + canvas.setTextAlignedCenter(); + canvas.setTextColor(cfontColor); + canvas.setFont(Font.mediumRoundedSystemFont(canvTextSize)); + canvas.setFont(this.provideFont("ultralight", 68)); + canvas.drawTextInRect(`${text}`, canvTextRect); + + return canvas.getImage(); + }; + + renderMedium = (widget) => { + const { + this_year_lunar_solar, + lunar_date, + animalEmoji, + meetDay, + data: { tmpBirth, ageYear, ageMonth, age, dayIcon }, + } = this.contentText; + + const phoneSize = Device.screenSize(); + const radio = phoneSize.width / phoneSize.height; + const containerStack = widget.addStack(); + containerStack.layoutHorizontally(); + + const leftStack = containerStack.addStack(); + leftStack.size = new Size(radio * widthMode, radio * heightMode); + let avatarImg = SFSymbol.named(`photo`).image; + if (this.FILE_MGR.fileExists(this.LEFT_IMG_KEY)) { + avatarImg = Image.fromFile(this.LEFT_IMG_KEY); + } + leftStack.backgroundImage = avatarImg; + containerStack.addSpacer(); + const rightStack = containerStack.addStack(); + rightStack.setPadding(0, 0, 0, 10); + rightStack.layoutVertically(); + rightStack.centerAlignContent(); + + rightStack.addSpacer(); + + const userStack = rightStack.addStack(); + userStack.layoutHorizontally(); + userStack.centerAlignContent(); + + const nameStack = userStack.addStack(); + nameStack.layoutVertically(); + + const userWidgetText = nameStack.addText(this.defaultData.username); + userWidgetText.textColor = this.widgetColor; + userWidgetText.font = this.provideFont("italic", mainTextSize + 10); + userWidgetText.shadowColor = new Color(this.defaultData.nicknameShadow); + userWidgetText.shadowOffset = new Point(3, 3); + userWidgetText.shadowRadius = 3; + + nameStack.addSpacer(5); + this.provideText(`相遇${meetDay}天`, nameStack, { + font: "Party Let", + size: mainTextSize, + opacity: 0.8, + }); + + userStack.addSpacer(); + + userStack.addImage(this.animalImg(animalEmoji)); + + rightStack.addSpacer(20); + if (tmpBirth.year > 0 && tmpBirth.month > 0 && tmpBirth.day > 0) { + this.rowCell(rightStack, { + icon: "hourglass", + color: "#1ab6f8", + title: "年龄", + text: `${ageYear + ageMonth} ${tmpBirth.day} 天`, + }); + } else { + this.rowCell(rightStack, { + icon: "hourglass", + color: "#1ab6f8", + title: "年龄", + text: age, + }); + } + rightStack.addSpacer(); + + this.rowCell(rightStack, { + icon: "calendar", + color: "#30d15b", + title: "农历", + text: `${lunar_date}`, + }); + + rightStack.addSpacer(); + + this.rowCell(rightStack, { + icon: "app.gift.fill", + color: "#fc6d6d", + title: "生日", + text: `${this_year_lunar_solar}`, + }); + + rightStack.addSpacer(); + + return widget; + }; + + renderSmall = (widget) => { + const { + this_year_lunar_solar, + lunar_date, + meetDay, + data: { tmpBirth, ageYear, ageMonth, age, dayIcon }, + } = this.contentText; + + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + + containerStack.addSpacer(); + + const topStack = containerStack.addStack(); + topStack.layoutHorizontally(); + topStack.centerAlignContent(); + + const avatarStack = topStack.addStack(); + let avatarImg = SFSymbol.named(`photo`).image; + if (this.FILE_MGR.fileExists(this.LEFT_IMG_KEY)) { + avatarImg = Image.fromFile(this.LEFT_IMG_KEY); + } + + avatarStack.backgroundImage = avatarImg; + avatarStack.size = new Size(42, 42); + avatarStack.cornerRadius = avatarStack.size.width / 2; + avatarStack.borderColor = Color.green(); + avatarStack.borderWidth = 1; + + topStack.addSpacer(20); + + const nameStack = topStack.addStack(); + nameStack.addSpacer(); + nameStack.layoutVertically(); + nameStack.centerAlignContent(); + + const userWidgetText = nameStack.addText(this.defaultData.username); + userWidgetText.textColor = this.widgetColor; + userWidgetText.font = this.provideFont("italic", 16); + userWidgetText.shadowColor = new Color(this.defaultData.nicknameShadow); + userWidgetText.shadowOffset = new Point(3, 3); + userWidgetText.shadowRadius = 3; + + nameStack.addSpacer(5); + this.provideText(`相遇${meetDay}天`, nameStack, { + font: "Party Let", + size: 12, + opacity: 0.8, + }); + + containerStack.addSpacer(); + + if (tmpBirth.year > 0 && tmpBirth.month > 0 && tmpBirth.day > 0) { + this.rowCell(containerStack, { + icon: "hourglass", + color: "#1ab6f8", + title: "年龄", + text: `${ageYear + ageMonth} ${tmpBirth.day} 天`, + }); + } else { + this.rowCell(containerStack, { + icon: "hourglass", + color: "#1ab6f8", + title: "年龄", + text: age, + }); + } + containerStack.addSpacer(); + + this.rowCell(containerStack, { + icon: "calendar", + color: "#30d15b", + title: "农历", + text: `${lunar_date}`, + }); + + containerStack.addSpacer(); + + this.rowCell(containerStack, { + icon: "app.gift.fill", + color: "#fc6d6d", + title: "生日", + text: `${this_year_lunar_solar}`, + }); + + containerStack.addSpacer(); + + return widget; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === "medium") { + widget.setPadding(0, 0, 0, 0); + return await this.renderMedium(widget); + } else if (this.widgetFamily === "large") { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget); //远程开发环境 diff --git a/Scripts/webo.js b/Scripts/webo.js index a706e31..f079bb9 100644 --- a/Scripts/webo.js +++ b/Scripts/webo.js @@ -77,8 +77,7 @@ class Widget extends DmYY { num.font = Font.lightSystemFont(10); num.textOpacity = 0.5; num.textColor = this.widgetColor; - - w.url = this.actionUrl('open-url', topic['scheme']); + body.url = topic['scheme']; return w; } /** @@ -128,7 +127,7 @@ class Widget extends DmYY { extr.font = Font.lightSystemFont(12); extr.textColor = this.widgetColor; extr.textOpacity = 0.6; - dom.url = this.actionUrl('open-url', topic['scheme']); + dom.url = topic['scheme']; bodyLeft.addSpacer(5); } body.addSpacer(); diff --git a/Scripts/wsgw.js b/Scripts/wsgw.js new file mode 100644 index 0000000..a8c1007 --- /dev/null +++ b/Scripts/wsgw.js @@ -0,0 +1,381 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: setting; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +/** + * 重写修改自作者 + * @channel https://t.me/yqc_123/ + * @feedback https://t.me/NobyDa_Chat + * @author 小白脸|𝐎𝐍𝐙𝟑𝐕 + * + * 添加重写:https://raw.githubusercontent.com/dompling/Script/master/wsgw/index.js + * + */ + +const defaultData = { + user: '**', + left: { + dayElePq: [], + balance: 0, + arrearsOfFees: false, + }, + right: { + previousBill: 0, + previousBillRate: 0, + thisYear: 0, + thisYearRate: 0, + }, + update: '', +}; + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = 'wsgw'; + this.name = '网上国网'; + this.userNum = args.widgetParameter || 0; + if (config.runsInApp) { + this.registerAction({ + icon: { name: 'photo.tv', color: '#5A74EF' }, + type: 'color', + title: '左侧背景', + desc: '左侧背景色', + val: 'leftColor', + }); + this.registerAction({ + icon: { name: 'arrow.clockwise', color: '#1890ff' }, + type: 'input', + title: '缓存时间', + desc: '默认3小时 (单位小时),填写方式数字', + placeholder: '3', + val: 'cacheTime', + }); + + this.registerAction({ + icon: { name: 'character.cursor.ibeam', color: '#EC6240' }, + type: 'input', + title: '文字缩放', + desc: '文字缩放比例,值为 0-1', + placeholder: '1', + val: 'scale', + }); + + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + date = new Date(); + day = this.date.getTime(); + + dataSource = { ...defaultData }; + + init = async () => { + console.log(`当前用户下标:${this.userNum}`); + this.cacheTime = (this.settings.cacheTime || 3) * 3600000; + this.scale = parseFloat(this.settings.scale || '1'); + if ( + !this.settings.data || + this.settings.cacheDay + this.cacheTime < this.day + ) { + console.log(`缓存失效,重新获取`); + await this.cacheData(); + } else { + console.log( + `最后更新时间:${new Date(parseInt(this.settings.cacheDay)).toLocaleString()}` + ); + console.log( + `缓存失效时间:${new Date(parseInt(this.settings.cacheDay) + this.cacheTime).toLocaleString()}` + ); + this.dataSource = { ...this.settings.data[this.userNum] }; + if (!this.dataSource.user) await this.cacheData(); + console.log(this.dataSource); + } + }; + + cacheData = async () => { + try { + const response = await this.$request.get( + 'https://api.wsgw-rewrite.com/electricity/bill/all', + { timeoutInterval: 60 } + ); + console.log(response); + this.settings.data = []; + response?.forEach((dataInfo) => { + const dataSource = { + user: '**', + left: { + dayElePq: [], + balance: 0, + arrearsOfFees: false, + }, + right: { + previousBill: 0, + previousBillRate: 0, + thisYear: 0, + thisYearRate: 0, + }, + update: '', + }; + + dataSource.user = dataInfo.userInfo.consName_dst.replaceAll('*', ''); + dataSource.left.balance = parseFloat(dataInfo.eleBill.sumMoney); + dataSource.left.dayElePq = dataInfo.dayElecQuantity.sevenEleList + .filter((item) => item.dayElePq !== '-') + .map((item) => ({ + label: item.day, + value: parseFloat(item.dayElePq), + })); + + dataSource.left.arrearsOfFees = dataInfo.arrearsOfFees; + + dataSource.right.previousBill = parseFloat( + this.last(dataInfo.monthElecQuantity?.mothEleList || []) + ?.monthEleCost || 0 + ); + + const oldVal = + this.last(dataInfo.monthElecQuantity?.mothEleList || [], 2) + ?.monthEleCost || 1; + + dataSource.right.previousBillRate = + ((dataSource.right.previousBill - oldVal) / oldVal) * 100; + + dataSource.right.previousBillRate = parseFloat( + dataSource.right.previousBillRate.toFixed(2) + ); + + dataSource.right.thisYear = parseFloat( + dataInfo.monthElecQuantity?.dataInfo?.totalEleCost || 0 + ); + + const lastYearVal = dataInfo.lastYearElecQuantity.dataInfo.totalEleCost; + + dataSource.right.thisYearRate = + ((dataSource.right.thisYear - lastYearVal) / lastYearVal) * 100; + + dataSource.right.thisYearRate = parseFloat( + dataSource.right.thisYearRate.toFixed(2) + ); + + dataSource.update = dataInfo.eleBill.date; + this.settings.data.push({ ...dataSource }); + }); + console.log(this.settings.data); + this.dataSource = { ...this.settings.data[this.userNum] }; + this.settings.cacheDay = this.day; + this.saveSettings(false); + } catch (e) { + console.log(`接口数据异常:请检查 BoxJS 重写`); + console.log(e); + } + }; + + last = (data = [], index = 1) => { + return data[data.length - index]; + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + barChart() { + return ` + { + "type": "bar", + "data": { + "labels": ${JSON.stringify(this.dataSource.left.dayElePq.map((item) => item.label).reverse())}, + "datasets": [ + { + "label": "Sales", + "data": ${JSON.stringify(this.dataSource.left.dayElePq.map((item) => parseFloat(item.value)).reverse())}, + "backgroundColor": "#fff", + "borderColor": "#fff", + "borderWidth": 1, + "borderRadius": { + "topLeft": 30, + "topRight": 30, + "bottomLeft": 30, // 只为柱状图底部设置圆角 + "bottomRight": 30 + }, + "barPercentage": 0.8, // 控制柱子的宽度 + "categoryPercentage": 0.4, + "borderSkipped": false // 应用自定义的圆角设置 + } + ] + }, + "options": { + "plugins": { + "legend": { + "display": false // 隐藏图例 + }, + "title": { + "display": false // 隐藏标题 + } + }, + "scales": { + "x": { + "display": false // 完全隐藏 X 轴 + }, + "y": { + "display": false // 完全隐藏 Y 轴 + } + }, + "layout": { + "padding": 0 // 移除图表周围的内边距 + } + } +} +`; + } + + createLeft = async (widget) => { + const fontStyle = { + color: new Color('#fff'), + size: 20 * this.scale, + opacity: 0.8, + }; + const leftStack = widget.addStack(); + leftStack.cornerRadius = 10; + leftStack.layoutVertically(); + leftStack.backgroundColor = new Color( + this.settings.leftColor || '#5A74EF', + 0.8 + ); + leftStack.setPadding(10, 10, 10, 10); + + const chartStack = leftStack.addStack(); + + const chartImage = await this.renderImage( + `https://quickchart.io/chart?v=4&w=800&h=400&f=png&c=${encodeURIComponent(this.barChart())}` + ); + const chartImageStack = chartStack.addImage(chartImage); + chartImageStack.imageSize = new Size(120, 60); + + leftStack.addSpacer(); + + this.provideText('余额', leftStack, fontStyle); + + const todayStack = leftStack.addStack(); + todayStack.centerAlignContent(); + if (this.dataSource.left.arrearsOfFees) + fontStyle.color = new Color('#f65755'); + + fontStyle.size = 20 * this.scale; + this.provideText('¥ ', todayStack, fontStyle); + + fontStyle.opacity = 1; + const todayUse = this.dataSource.left.balance; + + this.provideText(` ${todayUse.toLocaleString()}`, todayStack, fontStyle); + }; + + createDot = (stack, color) => { + const dotStack = stack.addStack(); + dotStack.setPadding(0, 0, 2, 0); + const dot = dotStack.addStack(); + + dot.size = new Size(10, 10); + dot.backgroundColor = new Color(color); + dot.cornerRadius = 10; + }; + + createCell = (widget, data = { title: '', num: 0, radio: 0 }) => { + const cellStack = widget.addStack(); + cellStack.backgroundColor = new Color('#404045'); + cellStack.setPadding(10, 10, 10, 10); + cellStack.cornerRadius = 10; + cellStack.layoutVertically(); + + const fontStyle = { + color: new Color('#fff'), + size: 14 * this.scale, + opacity: 0.6, + }; + this.provideText(data.title, cellStack, fontStyle); + + const dataStack = cellStack.addStack(); + dataStack.bottomAlignContent(); + + fontStyle.size = 12 * this.scale; + this.provideText('¥ ', dataStack, fontStyle); + + fontStyle.opacity = 1; + fontStyle.size = 20 * this.scale; + this.provideText(` ${data.num.toLocaleString()}`, dataStack, fontStyle); + dataStack.addSpacer(); + + const dotStack = dataStack.addStack(); + this.createDot(dotStack, data.radio > 0 ? '#7EEF8F' : '#ED86A5'); + + fontStyle.size = 12 * this.scale; + this.provideText( + data.radio > 0 ? ` +${data.radio}%` : ` -${Math.abs(data.radio)}%`, + dataStack, + fontStyle + ); + }; + + createRight = async (widget) => { + const rightStack = widget.addStack(); + rightStack.layoutVertically(); + this.createCell(rightStack, { + title: '上期费用', + num: this.dataSource.right.previousBill, + radio: this.dataSource.right.previousBillRate, + }); + rightStack.addSpacer(); + this.createCell(rightStack, { + title: '今年费用', + num: this.dataSource.right.thisYear, + radio: this.dataSource.right.thisYearRate, + }); + }; + + renderSmall = async (w) => { + w.setPadding(10, 10, 10, 10); + await this.createLeft(w); + return w; + }; + + renderMedium = async (w) => { + w.setPadding(10, 10, 10, 10); + const containerStack = w.addStack(); + containerStack.layoutHorizontally(); + await this.createLeft(containerStack); + containerStack.addSpacer(10); + await this.createRight(containerStack); + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.url = `com.wsgw.e.zsdl://platformapi/`; + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.notSupport(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget, '0', false); //远程开发环境 diff --git a/images/count.png b/images/count.png new file mode 100644 index 0000000..919c7fb Binary files /dev/null and b/images/count.png differ diff --git a/images/ftms.png b/images/ftms.png new file mode 100644 index 0000000..59c4bfe Binary files /dev/null and b/images/ftms.png differ diff --git a/images/gas-night.png b/images/gas-night.png new file mode 100644 index 0000000..74f0508 Binary files /dev/null and b/images/gas-night.png differ diff --git a/images/large.png b/images/large.png new file mode 100644 index 0000000..31eba02 Binary files /dev/null and b/images/large.png differ diff --git a/images/medium.png b/images/medium.png new file mode 100644 index 0000000..c336dca Binary files /dev/null and b/images/medium.png differ diff --git a/images/more.png b/images/more.png new file mode 100644 index 0000000..e1e36cb Binary files /dev/null and b/images/more.png differ diff --git a/images/small.png b/images/small.png new file mode 100644 index 0000000..b2fe824 Binary files /dev/null and b/images/small.png differ diff --git a/install.json b/install.json index 9e923ba..a8b7d63 100644 --- a/install.json +++ b/install.json @@ -5,29 +5,30 @@ "repo": "https://github.com/dompling/Scriptable", "apps": [ { - "version": "1.0.0", - "description": "单个安装,添加作者订阅,在 app 中运行,单个安装文件", - "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/widget.Install.js", - "thumb": "https://img.icons8.com/clouds/344/downloading-updates.png", - "name": "widget.Install", - "title": "单个安装" - }, - { - "version": "1.0.0", - "description": "全量安装", - "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/2YaInstall.js", - "thumb": "https://img.icons8.com/clouds/344/download-2.png", - "name": "2YaInstall", - "title": "全量安装" - }, - { - "version": "1.0.4", + "version": "1.1.2", "description": "DmYY组件库", "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js", "thumb": "https://img.icons8.com/clouds/344/settings.png", "name": "DmYY", "title": "DmYY", - "html":[ + "html": [ + "更新说明", + "v1.1.2", + "fix: 输入默认值问题", + "v1.1.1", + "feat: DmYY 判断有无 actions", + "v1.1.0", + "修复 boxjs 读取问题", + "v1.0.8", + "兼容 ios15 系统,图标引起脚本一直转圈问题", + "v1.0.7", + "处理背景图在 12mini 下错位的情况", + "v1.0.6", + "优化菜单,增加菜单图标", + "v1.0.5", + "修改调整菜单更多图标", + "预览关闭菜单功能", + "", "历史版本", "可视化界面版本" ] @@ -62,6 +63,12 @@ "html": [ "京东组件教程", "" + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } ] }, { @@ -91,7 +98,13 @@ "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/BiliBili.js", "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png", "name": "BiliBili", - "title": "今日番剧" + "title": "今日番剧", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { "version": "1.0.0", @@ -127,7 +140,13 @@ "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Calendar.js", "thumb": "https://img.icons8.com/clouds/344/edit-calendar.png", "name": "Calendar", - "title": "日历函数" + "title": "日历函数", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { "version": "1.0.0", @@ -148,7 +167,7 @@ ] }, { - "version": "1.0.0", + "version": "1.0.2", "description": "显示历史上的今日带图片情况", "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/HistoryToday.js", "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/historyToday.png", @@ -366,6 +385,12 @@ "", "Mobile登陆地址接口:", "请自行使用代理软件查看搜索即可,操作方式类似 PC 登陆地址的查看步骤" + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } ] }, { @@ -401,12 +426,24 @@ ] }, { - "version": "1.0.0", - "description": "显示定位信息的油价使用的情况", - "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/TodayOilPrice.js", + "version": "1.0.1", + "description": "获取当前省份油价", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Oild.js", "thumb": "https://img.icons8.com/clouds/344/engine-oil-level.png", "name": "TodayOilPrice", - "title": "今日油价" + "title": "今日油价", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ], + "html": [ + "今日油价", + "1.0.1 ", + "增加自动定位设置", + "增加省份选择" + ] }, { "version": "1.0.0", @@ -414,15 +451,92 @@ "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/COVID-19.js", "thumb": "https://img.icons8.com/clouds/344/coronavirus.png", "name": "COVID-19", - "title": "疫情日报" + "title": "疫情日报", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { - "version": "1.0.0", + "version": "1.0.1", "description": "显示公历和农历,根据近期的信息会出现一个倒计时的提示信息", "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/CountDown.js", "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/daysmatter.png", "name": "CountDownCalendar", - "title": "日历倒计时" + "title": "日历倒计时", + "html": [ + "更新说明", + "v1.0.1", + "调整大尺寸排列效果", + "调整下班倒计时默认不开启" + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "交管 12123 违章和扣分查询", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/12123.js", + "thumb": "https://is5-ssl.mzstatic.com/image/thumb/Purple112/v4/a7/a1/61/a7a16170-e644-079e-a362-9eb273becf9d/AppIcon-1x_U007emarketing-0-4-0-0-85-220.png/492x0w.webp", + "name": "12123", + "title": "交管 12123", + "html": [ + "Token获取说明", + "获取Token重写:Surge 12123重写模块 ", + "使用方法:配置重写规则,手动运行小组件,按提示跳转到 支付宝12123小程序 登录即可自动抓取/更新Token。", + "使用前,请确保您的代理APP已配置好BoxJs重写,BoxJs配置方法:BoxJS教程" + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "币种涨幅", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/PriceWidgets.js", + "thumb": "https://assets.coingecko.com/coins/images/1/large/bitcoin.png", + "name": "PriceWidgets", + "title": "网络货币", + "html": [ + "迁移自JSBox脚本", + "原项目:wuzeyou/PriceWidgets", + "特别感谢大佬:@Jackie Xiang" + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.2", + "description": "网上国网", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/wsgw.js", + "thumb": "https://is1-ssl.mzstatic.com/image/thumb/Purple116/v4/83/d8/8a/83d88a92-5a4d-7a2f-118c-80d795e7a9f6/AppIcon-0-0-1x_U007emarketing-0-5-0-0-sRGB-85-220.png/144x144.png", + "name": "wsgw", + "title": "网上国网", + "html": [ + "网上国网", + "1.填写账号密码BoxJS订阅", + "2.参考借用【@小白脸】大佬的重写和【@Yuheng0101】大佬的脚本", + "3.Surge 添加远程重写模块【网上国网重写】、其他的请自行使用 https://script.hub 转换" + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] } ] } diff --git a/package.json b/package.json index 409499c..7d7494d 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ ], "license": "ISC", "devDependencies": { - "@types/scriptable-ios": "^1.6.1" + "@types/scriptable-ios": "^1.6.1", + "prettier": "^3.3.3" } } diff --git a/yarn.lock b/yarn.lock index ebd5803..789a8f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6,3 +6,8 @@ version "1.6.1" resolved "https://registry.npm.taobao.org/@types/scriptable-ios/download/@types/scriptable-ios-1.6.1.tgz#44766b47a0c0c9f92a3c1bf46214288cf3d926f4" integrity sha1-RHZrR6DAyfkqPBv0YhQojPPZJvQ= + +prettier@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" + integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==