小组件:财联社电报

经验创意 · 155 次浏览
我的梦想捐钱修路建学校 创建于 2026-05-26 14:58

已失效 接口挂了

GPT老师改的Scriptable脚本

by:Yeetouu 

by:iMarkr

AI生成不一定完美,有朋友手动调整过的可把修改更完美的版本发出来给大伙用用

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>财联社电报</title>
  <style>
    html,
    body {
      margin: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
      background: transparent;
    }

    * {
      box-sizing: border-box;
    }

    body {
      font-family: "Microsoft YaHei", sans-serif;
      color: rgba(244, 248, 255, 0.94);
      -webkit-font-smoothing: antialiased;
      user-select: none;
    }

    button,
    input {
      font-family: inherit;
    }

    .card {
      position: relative;
      width: 100%;
      height: 100%;
      overflow: hidden;
      padding: clamp(12px, 3.7vw, 18px);
      border-radius: 18px;
      border: 1px solid rgba(130, 187, 255, 0.34);
      background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0.035) 35%, rgba(255, 255, 255, 0) 64%),
        radial-gradient(circle at 12% 0%, rgba(34, 197, 245, 0.23), transparent 35%),
        radial-gradient(circle at 100% 10%, rgba(239, 68, 68, 0.18), transparent 38%),
        linear-gradient(145deg, #101a28 0%, #07111f 56%, #151721 100%);
      box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.17),
        inset 0 -1px 0 rgba(255, 255, 255, 0.05),
        0 18px 44px rgba(0, 0, 0, 0.36);
      display: grid;
      grid-template-rows: auto auto 1fr auto;
      gap: clamp(7px, 2vh, 11px);
    }

    .topbar {
      min-width: 0;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px;
    }

    .brand {
      min-width: 0;
      display: flex;
      align-items: center;
      gap: 10px;
    }

    .mark {
      flex: 0 0 auto;
      width: clamp(32px, 8.2vw, 40px);
      height: clamp(32px, 8.2vw, 40px);
      border-radius: 12px;
      display: grid;
      place-items: center;
      overflow: hidden;
      background: rgba(255, 255, 255, 0.1);
      box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.3),
        0 10px 22px rgba(226, 61, 79, 0.24);
    }

    .mark img {
      width: 100%;
      height: 100%;
      display: block;
      object-fit: cover;
    }

    .mark.epdff-fixed-highlight img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }

    .titlebox {
      min-width: 0;
    }

    .title {
      margin: 0;
      font-size: clamp(16px, 4.2vw, 22px);
      line-height: 1.08;
      letter-spacing: 0;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .meta {
      margin-top: 4px;
      font-size: clamp(10px, 2.5vw, 12px);
      line-height: 1.25;
      color: rgba(200, 215, 238, 0.72);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .actions {
      flex: 0 0 auto;
      display: flex;
      align-items: center;
      gap: 8px;
      position: relative;
    }

    .icon-btn,
    .small-btn {
      border: 1px solid rgba(151, 197, 255, 0.28);
      color: rgba(244, 248, 255, 0.93);
      background: rgba(255, 255, 255, 0.08);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.13);
      cursor: pointer;
      transition: transform 140ms ease, border-color 140ms ease, background 140ms ease;
    }

    .icon-btn {
      width: clamp(30px, 7.8vw, 36px);
      height: clamp(30px, 7.8vw, 36px);
      padding: 0;
      border-radius: 12px;
      font-size: 16px;
      line-height: 1;
    }

    .small-btn {
      height: 32px;
      border-radius: 11px;
      padding: 0 10px;
      font-size: 12px;
      white-space: nowrap;
    }

    .icon-btn:hover,
    .small-btn:hover {
      border-color: rgba(174, 212, 255, 0.56);
      background: rgba(255, 255, 255, 0.13);
    }

    .icon-btn:active,
    .small-btn:active {
      transform: scale(0.94);
      background: rgba(255, 255, 255, 0.18);
    }

    .icon-btn:disabled {
      cursor: default;
      opacity: 0.55;
      transform: none;
    }

    .settings-wrap {
      position: relative;
    }

    .settings-menu {
      position: absolute;
      top: calc(100% + 8px);
      right: 0;
      width: min(214px, calc(100vw - 24px));
      padding: 10px;
      border-radius: 14px;
      border: 1px solid rgba(151, 197, 255, 0.3);
      background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.11), rgba(255, 255, 255, 0.04)),
        rgba(8, 18, 32, 0.96);
      box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.14),
        0 16px 30px rgba(0, 0, 0, 0.34);
      display: none;
      grid-template-columns: 1fr;
      gap: 8px;
      z-index: 5;
    }

    .settings-menu.is-open {
      display: grid;
    }

    .setting-row {
      min-width: 0;
      display: grid;
      grid-template-columns: 72px minmax(0, 1fr);
      gap: 8px;
      align-items: center;
      color: rgba(205, 219, 242, 0.78);
      font-size: 12px;
      line-height: 1.25;
    }

    .controls {
      min-width: 0;
      display: grid;
      grid-template-columns: minmax(0, 1fr);
      gap: 7px;
      align-items: center;
    }

    .field,
    .number {
      width: 100%;
      height: 32px;
      min-width: 0;
      border: 1px solid rgba(151, 197, 255, 0.22);
      border-radius: 11px;
      outline: none;
      color: rgba(244, 248, 255, 0.92);
      background: rgba(5, 12, 24, 0.34);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07);
      transition: border-color 140ms ease, background 140ms ease;
    }

    .field {
      padding: 0 10px;
      font-size: 12px;
    }

    .number {
      padding: 0 6px;
      text-align: center;
      font-size: 12px;
      font-variant-numeric: tabular-nums;
    }

    .field::placeholder {
      color: rgba(199, 214, 238, 0.52);
    }

    .field:hover,
    .field:focus,
    .number:hover,
    .number:focus {
      border-color: rgba(174, 212, 255, 0.54);
      background: rgba(255, 255, 255, 0.09);
    }

    .feed {
      min-height: 0;
      overflow-y: auto;
      overflow-x: hidden;
      padding-right: 3px;
      display: grid;
      align-content: start;
      gap: var(--item-gap, 4px);
    }

    .feed::-webkit-scrollbar {
      width: 6px;
    }

    .feed::-webkit-scrollbar-track {
      background: rgba(255, 255, 255, 0.04);
      border-radius: 999px;
    }

    .feed::-webkit-scrollbar-thumb {
      background: rgba(151, 178, 218, 0.38);
      border-radius: 999px;
    }

    .item {
      width: 100%;
      min-width: 0;
      height: auto;
      display: grid;
      grid-template-columns: 38px minmax(0, 1fr);
      gap: 8px;
      align-items: start;
      min-height: calc((var(--font-size, 12px) * 1.24) + 12px);
      padding: 5px 7px;
      border: 1px solid transparent;
      border-radius: 10px;
      color: inherit;
      text-align: left;
      background: rgba(255, 255, 255, 0.035);
      cursor: pointer;
      transition: transform 140ms ease, border-color 140ms ease, background 140ms ease;
    }

    .item.epdff-fixed-highlight {
      height: auto;
      min-height: calc((var(--font-size, 12px) * 1.24) + 12px);
      align-items: start;
    }

    .item:hover {
      border-color: rgba(159, 200, 255, 0.25);
      background: rgba(255, 255, 255, 0.08);
    }

    .item:active {
      transform: scale(0.987);
      background: rgba(255, 255, 255, 0.12);
    }

    .time {
      padding-top: 1px;
      color: var(--time-color, #c2c2c2);
      font-size: var(--font-size, 12px);
      line-height: 1.24;
      font-variant-numeric: tabular-nums;
      white-space: nowrap;
    }

    .headline {
      min-width: 0;
      color: var(--text-color, #ffffff);
      font-size: var(--font-size, 12px);
      line-height: 1.24;
      min-height: 1.24em;
      max-height: calc(var(--font-size, 12px) * 1.24 * var(--line-limit, 2));
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: var(--line-limit, 2);
      overflow: hidden;
      overflow-wrap: anywhere;
    }

    .foot {
      min-height: 16px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      color: rgba(195, 211, 236, 0.66);
      font-size: 11px;
      line-height: 1.25;
    }

    .status,
    .count {
      min-width: 0;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .count {
      flex: 0 0 auto;
    }

    @media (max-width: 380px), (max-height: 290px) {
      .card {
        padding: 10px;
        gap: 7px;
      }

      .controls {
        grid-template-columns: minmax(0, 1fr);
      }

      .meta {
        display: none;
      }

      .settings-menu {
        width: min(198px, calc(100vw - 20px));
      }

      .item {
        grid-template-columns: 36px minmax(0, 1fr);
        padding: 4px 6px;
      }
    }
  </style>
</head>
<body>
  <main class="card" aria-label="财联社电报浮岛组件">
    <header class="topbar">
      <div class="brand">
        <div class="mark" aria-hidden="true">
          <img src="https://image.cls.cn/share/cailianpress.png" alt="">
        </div>
        <div class="titlebox">
          <h1 class="title">财联社电报</h1>
          <div class="meta" id="updatedAt">本地兜底内容</div>
        </div>
      </div>
      <div class="actions">
        <div class="settings-wrap">
          <button class="icon-btn" id="settingsToggle" type="button" title="设置" aria-label="设置" aria-expanded="false">⚙</button>
          <div class="settings-menu" id="settingsMenu" aria-label="设置菜单">
            <label class="setting-row">
              <span>字体大小</span>
              <input class="number" id="fontSize" type="number" min="10" max="18" step="1" title="字体大小" aria-label="字体大小">
            </label>
            <label class="setting-row">
              <span>行数限制</span>
              <input class="number" id="lineLimit" type="number" min="1" max="5" step="1" title="行数限制" aria-label="行数限制">
            </label>
            <button class="small-btn" id="reset" type="button">重置设置</button>
          </div>
        </div>
        <button class="icon-btn" id="openHome" type="button" title="打开财联社" aria-label="打开财联社">↗</button>
        <button class="icon-btn" id="refresh" type="button" title="刷新电报" aria-label="刷新电报">⟳</button>
      </div>
    </header>

    <section class="controls" aria-label="显示设置">
      <input class="field" id="exclude" type="text" autocomplete="off" placeholder="过滤关键词或正则">
    </section>

    <section class="feed" id="feed" aria-live="polite"></section>

    <footer class="foot">
      <span class="status" id="status">等待浮岛宿主初始化</span>
      <span class="count" id="count">0 条</span>
    </footer>
  </main>

  <script>
    (function () {
      var API_URL = 'https://www.cls.cn/nodeapi/telegraphList';
      var HOME_URL = 'https://m.cls.cn';
      var defaults = {
        fontSize: 12,
        textColor: '#ffffff',
        timeColor: '#c2c2c2',
        lineLimit: 1,
        space: 4,
        exclude: ''
      };
      var state = {
        settings: Object.assign({}, defaults),
        items: [
          {
            title: '财联社电报组件已就绪',
            content: '财联社电报组件已就绪',
            ctime: Math.floor(Date.now() / 1000),
            shareurl: HOME_URL
          },
          {
            title: '等待浮岛宿主连接后自动刷新',
            content: '等待浮岛宿主连接后自动刷新',
            ctime: Math.floor(Date.now() / 1000),
            shareurl: HOME_URL
          },
          {
            title: '可设置过滤关键词、字号和标题行数',
            content: '可设置过滤关键词、字号和标题行数',
            ctime: Math.floor(Date.now() / 1000),
            shareurl: HOME_URL
          }
        ],
        updatedAt: '',
        hostReady: false,
        loading: false
      };

      var feedEl = document.getElementById('feed');
      var statusEl = document.getElementById('status');
      var countEl = document.getElementById('count');
      var updatedAtEl = document.getElementById('updatedAt');
      var refreshBtn = document.getElementById('refresh');
      var openHomeBtn = document.getElementById('openHome');
      var settingsToggleBtn = document.getElementById('settingsToggle');
      var settingsMenuEl = document.getElementById('settingsMenu');
      var resetBtn = document.getElementById('reset');
      var excludeInput = document.getElementById('exclude');
      var fontSizeInput = document.getElementById('fontSize');
      var lineLimitInput = document.getElementById('lineLimit');

      function invoke(method, args) {
        return window.fudao.invoke(method, args || {});
      }

      function pad(value) {
        return String(value).padStart(2, '0');
      }

      function nowText() {
        var date = new Date();
        return pad(date.getHours()) + ':' + pad(date.getMinutes());
      }

      function formatTime(value) {
        var number = Number(value);
        var date = Number.isFinite(number) && number > 0
          ? new Date(number * 1000)
          : new Date();
        return pad(date.getHours()) + ':' + pad(date.getMinutes());
      }

      function cleanText(value) {
        return String(value == null ? '' : value).replace(/\s+/g, ' ').trim();
      }

      function parseJSON(value, fallback) {
        try {
          if (typeof value !== 'string' || !value) return fallback;
          return JSON.parse(value);
        } catch (err) {
          return fallback;
        }
      }

      function clampNumber(value, min, max, fallback) {
        var number = Number(value);
        if (!Number.isFinite(number)) return fallback;
        return Math.min(max, Math.max(min, Math.round(number)));
      }

      function readState(key, defaultValue) {
        return invoke('state.read', {
          key: key,
          defaultValue: JSON.stringify(defaultValue)
        }).then(function (res) {
          return parseJSON(res, defaultValue);
        });
      }

      function writeState(key, value) {
        return invoke('state.write', {
          key: key,
          value: JSON.stringify(value)
        }).catch(function () {});
      }

      function normalizeSettings(input) {
        var next = Object.assign({}, defaults, input || {});
        next.fontSize = clampNumber(next.fontSize, 10, 18, defaults.fontSize);
        next.lineLimit = clampNumber(next.lineLimit, 1, 5, defaults.lineLimit);
        next.space = clampNumber(next.space, 0, 12, defaults.space);
        next.exclude = cleanText(next.exclude);
        next.textColor = /^#[0-9a-f]{6}$/i.test(next.textColor) ? next.textColor : defaults.textColor;
        next.timeColor = /^#[0-9a-f]{6}$/i.test(next.timeColor) ? next.timeColor : defaults.timeColor;
        return next;
      }

      function normalizeItems(payload) {
        var list = payload && payload.data && Array.isArray(payload.data.roll_data)
          ? payload.data.roll_data
          : [];
        return list.map(function (item) {
          var title = cleanText(item.title || item.content);
          if (!title) return null;
          return {
            title: title,
            content: cleanText(item.content || title),
            ctime: Number(item.ctime) || Math.floor(Date.now() / 1000),
            shareurl: cleanText(item.shareurl || HOME_URL)
          };
        }).filter(Boolean);
      }

      function filteredItems() {
        var list = state.items.slice();
        var pattern = state.settings.exclude;
        if (!pattern) return list;
        try {
          var re = new RegExp(pattern, 'i');
          return list.filter(function (item) {
            return !re.test(item.title || item.content || '');
          });
        } catch (err) {
          var keyword = pattern.toLowerCase();
          return list.filter(function (item) {
            return (item.title || item.content || '').toLowerCase().indexOf(keyword) === -1;
          });
        }
      }

      function setStatus(text) {
        statusEl.textContent = text;
      }

      function setSettingsMenu(open) {
        settingsMenuEl.classList.toggle('is-open', open);
        settingsToggleBtn.setAttribute('aria-expanded', open ? 'true' : 'false');
      }

      function applySettings() {
        var root = document.documentElement;
        root.style.setProperty('--font-size', state.settings.fontSize + 'px');
        root.style.setProperty('--line-limit', state.settings.lineLimit);
        root.style.setProperty('--item-gap', state.settings.space + 'px');
        root.style.setProperty('--text-color', state.settings.textColor);
        root.style.setProperty('--time-color', state.settings.timeColor);
        excludeInput.value = state.settings.exclude;
        fontSizeInput.value = state.settings.fontSize;
        lineLimitInput.value = state.settings.lineLimit;
      }

      function renderList() {
        var items = filteredItems();
        var fragment = document.createDocumentFragment();
        items.forEach(function (item) {
          var button = document.createElement('button');
          button.type = 'button';
          button.className = 'item';
          button.dataset.url = item.shareurl || HOME_URL;
          button.title = item.title;

          var time = document.createElement('span');
          time.className = 'time';
          time.textContent = formatTime(item.ctime);

          var headline = document.createElement('span');
          headline.className = 'headline';
          headline.textContent = item.title || item.content;

          button.appendChild(time);
          button.appendChild(headline);
          fragment.appendChild(button);
        });
        feedEl.replaceChildren(fragment);
        countEl.textContent = items.length + ' 条';
        updatedAtEl.textContent = state.updatedAt ? '更新于 ' + state.updatedAt : '本地兜底内容';
      }

      function render() {
        applySettings();
        renderList();
        refreshBtn.disabled = state.loading;
        refreshBtn.textContent = state.loading ? '…' : '⟳';
      }

      function saveSettings() {
        state.settings = normalizeSettings(state.settings);
        render();
        return writeState('settings', state.settings);
      }

      function loadCachedData() {
        return readState('telegraphCache', null).then(function (cache) {
          if (cache && Array.isArray(cache.items) && cache.items.length) {
            state.items = cache.items;
            state.updatedAt = cleanText(cache.updatedAt);
            render();
          }
        });
      }

      function refresh() {
        if (!state.hostReady || state.loading) return;
        state.loading = true;
        setStatus('正在刷新电报');
        render();

        invoke('http.get', {
          url: API_URL,
          headers: {},
          timeoutMs: 10000
        }).then(function (result) {
          if (!result || !result.ok) {
            var status = result && result.status ? 'HTTP ' + result.status : '请求失败';
            throw new Error(status);
          }
          var json = JSON.parse(result.text || '{}');
          var items = normalizeItems(json);
          if (!items.length) throw new Error('电报数据为空');
          state.items = items;
          state.updatedAt = nowText();
          setStatus('电报已更新');
          return writeState('telegraphCache', {
            items: state.items,
            updatedAt: state.updatedAt
          });
        }).catch(function () {
          setStatus(state.items.length ? '已显示上次可用数据' : '刷新失败');
        }).then(function () {
          state.loading = false;
          render();
        });
      }

      function openUrl(url) {
        if (!state.hostReady) return;
        invoke('url.open', {
          url: url || HOME_URL,
          closeAfterOpen: true
        }).catch(function () {
          setStatus('打开失败');
        });
      }

      function bindEvents() {
        settingsToggleBtn.addEventListener('click', function (event) {
          event.stopPropagation();
          setSettingsMenu(!settingsMenuEl.classList.contains('is-open'));
        });
        settingsMenuEl.addEventListener('click', function (event) {
          event.stopPropagation();
        });
        document.addEventListener('click', function () {
          setSettingsMenu(false);
        });
        refreshBtn.addEventListener('click', refresh);
        openHomeBtn.addEventListener('click', function () {
          openUrl(HOME_URL);
        });
        feedEl.addEventListener('click', function (event) {
          var item = event.target.closest('.item');
          if (!item) return;
          openUrl(item.dataset.url);
        });
        resetBtn.addEventListener('click', function () {
          state.settings = Object.assign({}, defaults);
          saveSettings();
        });
        excludeInput.addEventListener('input', function () {
          state.settings.exclude = excludeInput.value;
          renderList();
        });
        excludeInput.addEventListener('change', saveSettings);
        fontSizeInput.addEventListener('change', function () {
          state.settings.fontSize = fontSizeInput.value;
          saveSettings();
        });
        lineLimitInput.addEventListener('change', function () {
          state.settings.lineLimit = lineLimitInput.value;
          saveSettings();
        });
      }

      function initWhenReady() {
        if (!window.fudao || typeof window.fudao.invoke !== 'function') {
          setTimeout(initWhenReady, 120);
          return;
        }

        state.hostReady = true;
        setStatus('浮岛宿主已连接');
        Promise.all([
          readState('settings', defaults),
          loadCachedData()
        ]).then(function (values) {
          state.settings = normalizeSettings(values[0]);
          render();
          refresh();
        }).catch(function () {
          setStatus('读取状态失败,使用默认设置');
          render();
          refresh();
        });
      }

      bindEvents();
      render();
      setTimeout(initWhenReady, 0);
    })();
  </script>
</body>
</html>

我的梦想捐钱修路建学校 最后更新于 2026/5/29

回复内容
暂无回复
回复主贴