chore: 初始提交 - 移除所有硬编码凭据,统一从环境变量读取
@@ -0,0 +1,71 @@
|
||||
# ===== 开发依赖 =====
|
||||
node_modules/
|
||||
|
||||
# ===== Windows 独有 =====
|
||||
winform/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# ===== 运行时数据(服务器重新生成)=====
|
||||
data/
|
||||
logs/
|
||||
temp/
|
||||
.dbg/
|
||||
exports/
|
||||
backups/
|
||||
Toolbox-Weekly-Backup/
|
||||
|
||||
# ===== 前端大文件/素材(服务器不需要)=====
|
||||
public/images/korean_references/
|
||||
public/images/zhihu/
|
||||
public/images/wave_references/
|
||||
public/images/x_fashion/
|
||||
public/images/style_check_images/
|
||||
public/home-bg/
|
||||
public/tools/apple_watch/apple_health_export/
|
||||
public/download_files/
|
||||
|
||||
# ===== 上传文件 =====
|
||||
uploads/
|
||||
|
||||
# ===== 脚本 =====
|
||||
scripts/
|
||||
|
||||
# ===== 本地凭证(含密码/密钥,不上传)=====
|
||||
*.env.local
|
||||
config/
|
||||
public/tools/markets/auth_config.json
|
||||
|
||||
# ===== SSL 证书(服务器另有自己的)=====
|
||||
umersoft.com_OV_SSL/
|
||||
|
||||
# ===== 调试/临时文件 =====
|
||||
*.md
|
||||
*.txt
|
||||
cookies.txt
|
||||
_fix_*.js
|
||||
_verify*.js
|
||||
_verify2*.js
|
||||
check_*.js
|
||||
debug-*.md
|
||||
example_auto_*.js
|
||||
get_cookie_*.js
|
||||
OSS权限.txt
|
||||
.tmp_db_path.js
|
||||
tmp_*.js
|
||||
reproduce_issue.js
|
||||
verify_db*.js
|
||||
~\$*.docx
|
||||
|
||||
# ===== IIS/构建配置(服务器不需要)=====
|
||||
web.config
|
||||
webpack.config.js
|
||||
install_service_8977.bat
|
||||
uninstall_service_8977.bat
|
||||
|
||||
# ===== 杂项 =====
|
||||
.vscode/
|
||||
query
|
||||
service/
|
||||
/tools/
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "yangs-toolbox",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"main": "src/server/index.js",
|
||||
"engines": {
|
||||
"node": ">=22 <24"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "\"D:\\ProgramData\\nodejs_22\\node.exe\" src/server/index.js",
|
||||
"start22": "\"D:\\ProgramData\\nodejs_22\\node.exe\" src/server/index.js",
|
||||
"start:5051": "set PORT=5051&& \"D:\\ProgramData\\nodejs_22\\node.exe\" src/server/index.js",
|
||||
"start:8976": "set PORT=8976&& \"D:\\ProgramData\\nodejs_22\\node.exe\" src/server/index.js",
|
||||
"start:8977": "set PORT=8977&& \"D:\\ProgramData\\nodejs_22\\node.exe\" src/server/index.js",
|
||||
"upgrade:expense": "\"D:\\ProgramData\\nodejs_22\\node.exe\" scripts/upgrade_expense_db.js",
|
||||
"test": "\"D:\\ProgramData\\nodejs_22\\node.exe\" src/server/test_weekly_modular.js",
|
||||
"lint": "powershell -NoProfile -Command \"Get-ChildItem -Recurse -Filter *.js -Path src,public | Where-Object { $_.FullName -notmatch '\\\\node_modules\\\\' -and $_.FullName -notmatch '\\\\public\\\\lib\\\\' -and $_.FullName -notmatch '\\\\public\\\\tools\\\\cloud_notes\\\\tinymce\\\\' -and $_.FullName -notmatch '\\\\public\\\\tools\\\\rebalance\\\\lib\\\\' -and $_.Name -notmatch '\\.min\\.js$' } | ForEach-Object { & 'D:\\\\ProgramData\\\\nodejs_22\\\\node.exe' --check $_.FullName }\"",
|
||||
"typecheck": "npm run lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.8.0",
|
||||
"@univerjs/presets": "^0.19.0",
|
||||
"@univerjs/protocol": "^0.1.48",
|
||||
"@univerjs/ui": "^0.19.0",
|
||||
"archiver": "^7.0.1",
|
||||
"axios": "^1.7.5",
|
||||
"better-sqlite3": "^9.4.5",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"imapflow": "^1.3.3",
|
||||
"mailparser": "^3.9.8",
|
||||
"mammoth": "^1.12.0",
|
||||
"multer": "^2.0.2",
|
||||
"nodemailer": "^6.9.8",
|
||||
"pdf-parse": "^2.4.5",
|
||||
"puppeteer": "^24.40.0",
|
||||
"puppeteer-extra": "^3.3.6",
|
||||
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"unzipper": "^0.10.14",
|
||||
"word-extractor": "^1.0.4",
|
||||
"ws": "^8.18.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^7.1.4",
|
||||
"esbuild": "^0.27.4",
|
||||
"style-loader": "^4.0.0",
|
||||
"tinymce": "^6.8.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
(() => {
|
||||
const originalFetch = window.fetch
|
||||
window.fetch = (input, init = {}) => {
|
||||
const opts = Object.assign({}, init)
|
||||
if (!opts.credentials) opts.credentials = 'include'
|
||||
return originalFetch(input, opts)
|
||||
}
|
||||
const overlayId = 'authOverlay'
|
||||
const ensureOverlay = () => {
|
||||
let ov = document.getElementById(overlayId)
|
||||
if (ov) return ov
|
||||
ov = document.createElement('div')
|
||||
ov.id = overlayId
|
||||
ov.style.position = 'fixed'
|
||||
ov.style.left = '0'; ov.style.top = '0'; ov.style.right = '0'; ov.style.bottom = '0'
|
||||
ov.style.display = 'none'
|
||||
ov.style.alignItems = 'center'
|
||||
ov.style.justifyContent = 'center'
|
||||
ov.style.background = 'rgba(0,0,0,0.6)'
|
||||
ov.style.zIndex = '9999'
|
||||
const panel = document.createElement('div')
|
||||
panel.style.background = '#fff'
|
||||
panel.style.padding = '20px'
|
||||
panel.style.borderRadius = '8px'
|
||||
panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)'
|
||||
const title = document.createElement('div')
|
||||
title.textContent = '鉴权失败,请重新校验'
|
||||
title.style.fontSize = '16px'
|
||||
title.style.marginBottom = '12px'
|
||||
title.style.color = '#333'
|
||||
const debug = document.createElement('pre')
|
||||
debug.id = 'authDebug'
|
||||
debug.style.fontSize = '12px'
|
||||
debug.style.lineHeight = '1.5'
|
||||
debug.style.background = '#f7f7f8'
|
||||
debug.style.border = '1px solid #e3e3e7'
|
||||
debug.style.padding = '10px'
|
||||
debug.style.margin = '0 0 12px 0'
|
||||
debug.style.maxWidth = '520px'
|
||||
debug.style.maxHeight = '240px'
|
||||
debug.style.overflow = 'auto'
|
||||
debug.textContent = '正在收集诊断信息...'
|
||||
const btn = document.createElement('button')
|
||||
btn.textContent = '重新校验'
|
||||
btn.style.padding = '8px 16px'
|
||||
btn.addEventListener('click', async () => {
|
||||
await handshake()
|
||||
})
|
||||
panel.appendChild(title)
|
||||
panel.appendChild(debug)
|
||||
panel.appendChild(btn)
|
||||
ov.appendChild(panel)
|
||||
document.body.appendChild(ov)
|
||||
return ov
|
||||
}
|
||||
const showOverlay = () => { ensureOverlay().style.display = 'flex' }
|
||||
const hideOverlay = () => { const ov = ensureOverlay(); ov.style.display = 'none' }
|
||||
let cfg = { enableCookieAuth: true, cookieName: 'rebalance_jwt' }
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
const res = await fetch('/assets/auth_config.json', { cache: 'no-store' })
|
||||
if (!res.ok) return
|
||||
const data = await res.json()
|
||||
cfg = Object.assign({}, cfg, data || {})
|
||||
} catch (_) {}
|
||||
}
|
||||
const debugState = {
|
||||
url: '',
|
||||
config: {},
|
||||
tokenInUrl: false,
|
||||
tokenCall: { tried: false, ok: false, status: 0 },
|
||||
handshake: { tried: false, ok: false, status: 0 },
|
||||
server: {}
|
||||
}
|
||||
const removeTokenFromUrl = () => {
|
||||
try {
|
||||
const nu = new URL(window.location.href)
|
||||
nu.searchParams.delete('token')
|
||||
window.history.replaceState(null, document.title, nu.toString())
|
||||
} catch (_) {}
|
||||
}
|
||||
const handleTokenInUrl = async () => {
|
||||
try {
|
||||
const u = new URL(window.location.href)
|
||||
const token = u.searchParams.get('token')
|
||||
debugState.url = u.toString()
|
||||
debugState.config = { enableCookieAuth: !!cfg.enableCookieAuth, cookieName: String(cfg.cookieName || '') }
|
||||
debugState.tokenInUrl = !!token
|
||||
if (token) {
|
||||
if (cfg.enableCookieAuth) {
|
||||
const r = await fetch(`/auth/token?token=${encodeURIComponent(token)}`, { method: 'GET' })
|
||||
debugState.tokenCall.tried = true
|
||||
debugState.tokenCall.ok = r.ok
|
||||
debugState.tokenCall.status = r.status || 0
|
||||
removeTokenFromUrl()
|
||||
return r.ok
|
||||
} else {
|
||||
removeTokenFromUrl()
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
return true
|
||||
}
|
||||
const handshake = async () => {
|
||||
if (!cfg.enableCookieAuth) { hideOverlay(); return true }
|
||||
try {
|
||||
const r = await fetch('/auth/handshake', { cache: 'no-store' })
|
||||
debugState.handshake.tried = true
|
||||
debugState.handshake.ok = r.ok
|
||||
debugState.handshake.status = r.status || 0
|
||||
/*
|
||||
try {
|
||||
const dbgRes = await fetch('/auth/debug', { cache: 'no-store' })
|
||||
try {
|
||||
const body = await dbgRes.json()
|
||||
debugState.server = body
|
||||
} catch (_) {
|
||||
debugState.server = { error: 'debug parse failed', status: dbgRes.status || 0 }
|
||||
}
|
||||
} catch (e) {
|
||||
debugState.server = { error: 'debug request failed' }
|
||||
}
|
||||
*/
|
||||
const box = document.getElementById('authDebug')
|
||||
if (box) {
|
||||
const lines = []
|
||||
lines.push(`URL: ${debugState.url}`)
|
||||
lines.push(`Config: enableCookieAuth=${debugState.config.enableCookieAuth} cookieName=${debugState.config.cookieName}`)
|
||||
lines.push(`TokenInUrl: ${debugState.tokenInUrl}`)
|
||||
lines.push(`TokenCall: tried=${debugState.tokenCall.tried} ok=${debugState.tokenCall.ok} status=${debugState.tokenCall.status}`)
|
||||
lines.push(`Handshake: tried=${debugState.handshake.tried} ok=${debugState.handshake.ok} status=${debugState.handshake.status}`)
|
||||
if (debugState.server && typeof debugState.server === 'object') {
|
||||
lines.push(`Server.enableAuth: ${debugState.server.enableAuth}`)
|
||||
lines.push(`Server.jwkLoaded: ${debugState.server.jwkLoaded}`)
|
||||
lines.push(`Server.cookieName: ${debugState.server.cookieName}`)
|
||||
lines.push(`Server.cookiePresent: ${debugState.server.cookiePresent}`)
|
||||
lines.push(`Server.verified: ${debugState.server.verified}`)
|
||||
lines.push(`Server.keysCount: ${debugState.server.keysCount}`)
|
||||
try {
|
||||
const ks = Array.isArray(debugState.server.keysSources) ? debugState.server.keysSources : []
|
||||
lines.push(`Server.keysSources: ${JSON.stringify(ks)}`)
|
||||
} catch (_) {}
|
||||
try {
|
||||
const th = debugState.server.tokenHeader || {}
|
||||
const tp = debugState.server.tokenPayload || {}
|
||||
lines.push(`Server.tokenHeader.alg: ${th.alg || ''}`)
|
||||
lines.push(`Server.tokenPayload.iss: ${tp.iss || ''}`)
|
||||
lines.push(`Server.tokenPayload.aud: ${tp.aud || ''}`)
|
||||
lines.push(`Server.tokenPayload.exp: ${tp.exp || ''}`)
|
||||
} catch (_) {}
|
||||
if (debugState.server.error) {
|
||||
lines.push(`Server.error: ${debugState.server.error}`)
|
||||
}
|
||||
const rq = debugState.server.request || {}
|
||||
lines.push(`Server.request.host: ${rq.host || ''}`)
|
||||
lines.push(`Server.request.path: ${rq.path || ''}`)
|
||||
lines.push(`Server.request.origin: ${rq.origin || ''}`)
|
||||
lines.push(`Server.request.secFetchMode: ${rq.secFetchMode || ''}`)
|
||||
lines.push(`Server.request.forwardedProto: ${rq.forwardedProto || ''}`)
|
||||
lines.push(`Server.request.secure: ${rq.secure}`)
|
||||
lines.push(`Server.request.proto: ${rq.proto || ''}`)
|
||||
} else {
|
||||
lines.push(`Server: ${String(debugState.server)}`)
|
||||
}
|
||||
lines.push(`Expected URLs: /auth/token?token=... , /auth/handshake , /api/...`)
|
||||
box.textContent = lines.join('\n')
|
||||
}
|
||||
if (r.ok) { hideOverlay(); return true }
|
||||
showOverlay(); return false
|
||||
} catch (_) { showOverlay(); return false }
|
||||
}
|
||||
const init = async () => {
|
||||
await loadConfig()
|
||||
await handleTokenInUrl()
|
||||
await handshake()
|
||||
}
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
init()
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', init)
|
||||
}
|
||||
})()
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"_comment": "公共静态资源下,与 Rebalance 相关的 JWT 鉴权配置",
|
||||
"cookieName_comment": "用于识别已登录用户的 JWT cookie 名称",
|
||||
"cookieName": "rebalance_jwt",
|
||||
"enableCookieAuth_comment": "true:启用基于 cookie 的鉴权;false:不校验登录状态",
|
||||
"enableCookieAuth": true
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
* { box-sizing: border-box }
|
||||
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif }
|
||||
.topbar { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; border-bottom: 1px solid #ddd; position: sticky; top: 0; background: #fff; z-index: 10 }
|
||||
.tabs { display: flex; gap: 8px }
|
||||
.tab { border: none; background: #f5f5f5; padding: 8px 12px; border-radius: 8px; cursor: pointer; text-decoration: none; color: inherit }
|
||||
.tab.active { background: #222; color: #fff }
|
||||
.settings { border: none; background: #fff; padding: 6px 10px; border: 1px solid #222; border-radius: 6px; cursor: pointer }
|
||||
main { padding: 12px }
|
||||
.modal { position: fixed; inset: 0; display: grid; place-items: center; background: rgba(0,0,0,0.35); z-index: 1000 }
|
||||
.hidden { display: none }
|
||||
.modal-body { background: #fff; width: 520px; max-width: 90vw; border-radius: 10px; padding: 16px; display: grid; gap: 8px }
|
||||
.modal-actions { display: flex; justify-content: flex-end; gap: 8px }
|
||||
mark { background: #ffe58f }
|
||||
.kw-0 { background: #ffe58f }
|
||||
.kw-1 { background: #ffd6e7 }
|
||||
.kw-2 { background: #d6f5ff }
|
||||
.kw-3 { background: #d9f7be }
|
||||
.kw-4 { background: #fff1b8 }
|
||||
.kw-5 { background: #efdbff }
|
||||
@@ -0,0 +1,61 @@
|
||||
const el = id => document.getElementById(id)
|
||||
const highlight = (text, terms) => {
|
||||
if (!terms || terms.length === 0) return text
|
||||
let s = text
|
||||
terms.forEach((t, i) => {
|
||||
const cls = `kw-${i % 6}`
|
||||
const re = new RegExp(`(${t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi')
|
||||
s = s.replace(re, `<mark class="${cls}">$1</mark>`)
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
const fmtCNDateTime = (d) => {
|
||||
const date = d instanceof Date ? d : new Date()
|
||||
try {
|
||||
const s = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).format(date)
|
||||
return s.replace(/\//g, '-').replace(/\u200E/g, '')
|
||||
} catch {
|
||||
const pad = n => String(n).padStart(2, '0')
|
||||
const y = date.getFullYear(), m = pad(date.getMonth()+1), day = pad(date.getDate()), hh = pad(date.getHours()), mm = pad(date.getMinutes()), ss = pad(date.getSeconds())
|
||||
return `${y}-${m}-${day} ${hh}:${mm}:${ss}`
|
||||
}
|
||||
}
|
||||
|
||||
const fmtCNSimpleTime = (d) => {
|
||||
const date = d instanceof Date ? d : new Date()
|
||||
try {
|
||||
const s = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', hour: '2-digit', minute: '2-digit', hour12: false }).format(date)
|
||||
return s.replace(/\u200E/g, '')
|
||||
} catch {
|
||||
const pad = n => String(n).padStart(2, '0')
|
||||
return `${pad(date.getHours())}:${pad(date.getMinutes())}`
|
||||
}
|
||||
}
|
||||
|
||||
const cnDateStr = (d) => {
|
||||
const date = d instanceof Date ? d : new Date()
|
||||
try {
|
||||
const s = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit' }).format(date)
|
||||
return s.replace(/\//g, '-').replace(/\u200E/g, '')
|
||||
} catch {
|
||||
const pad = n => String(n).padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())}`
|
||||
}
|
||||
}
|
||||
|
||||
window.fmtCNDateTime = fmtCNDateTime
|
||||
window.fmtCNSimpleTime = fmtCNSimpleTime
|
||||
window.cnDateStr = cnDateStr
|
||||
|
||||
;(function handleSetToken() {
|
||||
try {
|
||||
const u = new URL(window.location.href)
|
||||
const token = u.searchParams.get('set_token')
|
||||
if (token) {
|
||||
localStorage.setItem('toolbox_creds_token', token)
|
||||
u.searchParams.delete('set_token')
|
||||
window.history.replaceState(null, '', u.toString())
|
||||
}
|
||||
} catch {}
|
||||
})()
|
||||
@@ -0,0 +1,16 @@
|
||||
<header class="topbar">
|
||||
<nav class="tabs">
|
||||
<a class="tab" href="/go?systemId=Tools-weibo" data-key="weibo">微博跟踪</a>
|
||||
<a class="tab" href="/go?systemId=Tools-markets" data-key="markets">市场跟踪</a>
|
||||
<a class="tab" href="/go?systemId=Tools-wall-" data-key="wall">告知墙</a>
|
||||
<a class="tab" href="/go?systemId=Tools-funds" data-key="funds">本金变动池</a>
|
||||
<a class="tab" href="/go?systemId=Tools-ccb_private_funds" data-key="ccb_private_funds">建行私募理财</a>
|
||||
<a class="tab" href="/go?systemId=Tools-cloud" data-key="cloud">云产品管理</a>
|
||||
<a class="tab" href="/go?systemId=Tools-weekly" data-key="weekly">工作周报</a>
|
||||
<a class="tab" href="/go?systemId=TRAE-Rebalance&url=https%3A%2F%2Ftraesite.umersoft.com%3A8960%2FRebalance.html" data-key="rebalance">再平衡</a>
|
||||
<a class="tab" href="/go?systemId=Wager&url=https%3A%2F%2Ftraesite.umersoft.com%3A8956" data-key="wager">投注</a>
|
||||
<a class="tab" href="/go?systemId=TRAE-PSC&url=https%3A%2F%2Ftraesite.umersoft.com%3A8969%2F" data-key="disk">安全网盘</a>
|
||||
<a class="tab" href="/go?systemId=FitnessDiary" data-key="fitness">运动日志</a>
|
||||
<a class="tab" href="/go?systemId=CalendarReminder&url=https%3A%2F%2Ftraesite.umersoft.com%3A8972%2F" data-key="fitness">日历提醒</a>
|
||||
</nav>
|
||||
</header>
|
||||
@@ -0,0 +1,26 @@
|
||||
(async function(){
|
||||
try {
|
||||
const wrap = document.querySelector('#topbar')
|
||||
if (!wrap) return
|
||||
const res = await fetch('/common/nav.html')
|
||||
const html = await res.text()
|
||||
wrap.innerHTML = html
|
||||
const path = location.pathname.toLowerCase()
|
||||
let key = ''
|
||||
if (path.includes('/tools/weibo/')) key = 'weibo'
|
||||
else if (path.includes('/tools/markets/')) key = 'markets'
|
||||
else if (path.includes('/tools/wall/')) key = 'wall'
|
||||
else if (path.includes('/tools/funds/')) key = 'funds'
|
||||
else if (path.includes('/tools/ccb_private_funds/')) key = 'ccb_private_funds'
|
||||
else if (path.includes('/tools/cloud/')) key = 'cloud'
|
||||
else if (path.includes('/tools/weekly/')) key = 'weekly'
|
||||
else if (path.includes('/tools/ai-lib/')) key = 'ai-lib'
|
||||
else if (path.includes('/tools/expense/')) key = 'expense'
|
||||
if (key) {
|
||||
wrap.querySelectorAll('a.tab').forEach(a => {
|
||||
if (a.dataset.key === key) a.classList.add('active')
|
||||
else a.classList.remove('active')
|
||||
})
|
||||
}
|
||||
} catch {}
|
||||
})()
|
||||
@@ -0,0 +1,128 @@
|
||||
[
|
||||
|
||||
{
|
||||
"name": "地下偶像相关揭示板",
|
||||
"url": "https://m.weibo.cn/u/7716940453",
|
||||
"icon": "text_italic.png"
|
||||
},{
|
||||
"name": "世界树剧场",
|
||||
"url": "https://m.weibo.cn/u/7925328421",
|
||||
"icon": "user_go.png"
|
||||
},
|
||||
{
|
||||
"name": "育音堂",
|
||||
"url": "https://weibo.com/u/1802208671",
|
||||
"icon": "weather_sun.png"
|
||||
},
|
||||
{
|
||||
"name": "X下载利器",
|
||||
"url": "https://ssstwitter.com/",
|
||||
"icon": "twitter_tool.png"
|
||||
},
|
||||
{
|
||||
"name": "上海地下偶像联盟",
|
||||
"url": "https://m.weibo.cn/u/7912812433",
|
||||
"icon": "weibo.png"
|
||||
},
|
||||
{
|
||||
"name": "免费ICON",
|
||||
"url": "https://igoutu.cn/icons",
|
||||
"icon": ""
|
||||
},
|
||||
|
||||
{
|
||||
"name": "培训/导航/LOGO",
|
||||
"url": "http://112.74.31.221:8888/default.html",
|
||||
"icon": "HDA.png"
|
||||
},
|
||||
{
|
||||
"name": "Git 服务器",
|
||||
"url": "http://47.101.56.101:8998/Home/LogOn?ReturnUrl=%2F",
|
||||
"icon": "HDA.png"
|
||||
},
|
||||
{
|
||||
"name": "禅道 项目系统",
|
||||
"url": "http://112.74.31.221:82/zentao/user-login-L3plbnRhby9teS5odG1s.html",
|
||||
"icon": "HDA.png"
|
||||
},
|
||||
{
|
||||
"name": "阿里云-合规报告",
|
||||
"url": "https://security.aliyun.com/trust-center/compliance?spm=5176.32436255.J_JIpOXHuIiVOM6_FDQtQLN.2.377618c54gQlJa",
|
||||
"icon": "aliyun.png"
|
||||
},
|
||||
{
|
||||
"name": "硅谷-阿里合同",
|
||||
"url": "https://traesite.umersoft.com:8975/download_files/%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%8D%8F%E8%AE%AE.pdf",
|
||||
"icon": "aliyun.png"
|
||||
},
|
||||
{
|
||||
"name": "在线MP3播放",
|
||||
"url": "https://music.cpp-prog.com",
|
||||
"icon": "music.png"
|
||||
},
|
||||
{
|
||||
"name": "上海市医疗器械追溯报告系统",
|
||||
"url": "https://zssb.yjj.sh.gov.cn",
|
||||
"icon": "GOV.png"
|
||||
},
|
||||
{
|
||||
"name": "虚拟显示器,USB-MMIDD 驱动",
|
||||
"url": "https://traesite.umersoft.com:8975/download_files/usbmmidd_v2.zip",
|
||||
"icon": "download.png"
|
||||
},
|
||||
{
|
||||
"name": "CHROME纯净版X64",
|
||||
"url": "https://traesite.umersoft.com:8975/download_files/ChromeStandaloneSetup64.exe",
|
||||
"icon": "download.png"
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "测试长标题如果不指定图标",
|
||||
"url": "#",
|
||||
"icon": ""
|
||||
}
|
||||
]
|
||||
|
After Width: | Height: | Size: 693 B |
|
After Width: | Height: | Size: 477 B |
|
After Width: | Height: | Size: 240 B |
|
After Width: | Height: | Size: 476 B |
|
After Width: | Height: | Size: 131 B |
|
After Width: | Height: | Size: 697 B |
|
After Width: | Height: | Size: 460 B |
|
After Width: | Height: | Size: 762 B |
|
After Width: | Height: | Size: 796 B |
|
After Width: | Height: | Size: 769 B |
|
After Width: | Height: | Size: 577 B |
|
After Width: | Height: | Size: 588 B |
|
After Width: | Height: | Size: 813 B |
|
After Width: | Height: | Size: 516 B |
|
After Width: | Height: | Size: 476 B |
|
After Width: | Height: | Size: 517 B |
|
After Width: | Height: | Size: 696 B |
|
After Width: | Height: | Size: 488 B |
|
After Width: | Height: | Size: 541 B |
|
After Width: | Height: | Size: 356 B |
|
After Width: | Height: | Size: 544 B |
|
After Width: | Height: | Size: 658 B |
|
After Width: | Height: | Size: 740 B |
|
After Width: | Height: | Size: 663 B |
|
After Width: | Height: | Size: 207 B |
|
After Width: | Height: | Size: 860 B |
|
After Width: | Height: | Size: 427 B |
|
After Width: | Height: | Size: 459 B |
|
After Width: | Height: | Size: 479 B |
|
After Width: | Height: | Size: 432 B |
|
After Width: | Height: | Size: 541 B |
|
After Width: | Height: | Size: 590 B |
|
After Width: | Height: | Size: 581 B |
|
After Width: | Height: | Size: 441 B |
|
After Width: | Height: | Size: 512 B |
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 408 B |
|
After Width: | Height: | Size: 531 B |
|
After Width: | Height: | Size: 549 B |
|
After Width: | Height: | Size: 553 B |
|
After Width: | Height: | Size: 591 B |
|
After Width: | Height: | Size: 408 B |
|
After Width: | Height: | Size: 455 B |
|
After Width: | Height: | Size: 730 B |
|
After Width: | Height: | Size: 488 B |
|
After Width: | Height: | Size: 763 B |
|
After Width: | Height: | Size: 468 B |
|
After Width: | Height: | Size: 447 B |
|
After Width: | Height: | Size: 528 B |
|
After Width: | Height: | Size: 516 B |
|
After Width: | Height: | Size: 490 B |
|
After Width: | Height: | Size: 482 B |
|
After Width: | Height: | Size: 578 B |
|
After Width: | Height: | Size: 473 B |
|
After Width: | Height: | Size: 723 B |
|
After Width: | Height: | Size: 507 B |
|
After Width: | Height: | Size: 415 B |
|
After Width: | Height: | Size: 474 B |
|
After Width: | Height: | Size: 542 B |
|
After Width: | Height: | Size: 494 B |
|
After Width: | Height: | Size: 738 B |
|
After Width: | Height: | Size: 873 B |
|
After Width: | Height: | Size: 506 B |
|
After Width: | Height: | Size: 462 B |
|
After Width: | Height: | Size: 643 B |
|
After Width: | Height: | Size: 736 B |
|
After Width: | Height: | Size: 618 B |
|
After Width: | Height: | Size: 382 B |
|
After Width: | Height: | Size: 537 B |
|
After Width: | Height: | Size: 549 B |
|
After Width: | Height: | Size: 607 B |
|
After Width: | Height: | Size: 743 B |
|
After Width: | Height: | Size: 741 B |
|
After Width: | Height: | Size: 958 B |
|
After Width: | Height: | Size: 669 B |
|
After Width: | Height: | Size: 743 B |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 781 B |
|
After Width: | Height: | Size: 733 B |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 523 B |
|
After Width: | Height: | Size: 464 B |
|
After Width: | Height: | Size: 619 B |
|
After Width: | Height: | Size: 524 B |
|
After Width: | Height: | Size: 610 B |
|
After Width: | Height: | Size: 533 B |
|
After Width: | Height: | Size: 703 B |
|
After Width: | Height: | Size: 656 B |
|
After Width: | Height: | Size: 467 B |