Compare commits
4
Commits
bbbc32142d
...
5342c5e489
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5342c5e489 | ||
|
|
efe54d6a41 | ||
|
|
9b29dff067 | ||
|
|
2036c9c89a |
@@ -25,6 +25,7 @@ vendor/
|
|||||||
|
|
||||||
# Build
|
# Build
|
||||||
u-desktop
|
u-desktop
|
||||||
|
dist/
|
||||||
|
|
||||||
# Web UI
|
# Web UI
|
||||||
web-ui/node_modules/
|
web-ui/node_modules/
|
||||||
|
|||||||
@@ -13,15 +13,26 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const bingAPIBase = "https://www.bing.com/HPImageArchive.aspx?format=js&n=8&mkt=zh-CN"
|
const bingAPIPrefix = "https://www.bing.com/HPImageArchive.aspx?format=js&n=8"
|
||||||
|
|
||||||
|
type bingImage struct {
|
||||||
|
StartDate string `json:"startdate"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
URLBase string `json:"urlbase"`
|
||||||
|
Copyright string `json:"copyright"`
|
||||||
|
}
|
||||||
|
|
||||||
type bingResponse struct {
|
type bingResponse struct {
|
||||||
Images []struct {
|
Images []bingImage `json:"images"`
|
||||||
StartDate string `json:"startdate"`
|
}
|
||||||
URL string `json:"url"`
|
|
||||||
URLBase string `json:"urlbase"`
|
type biturlResponse struct {
|
||||||
Copyright string `json:"copyright"`
|
StartDate string `json:"start_date"`
|
||||||
} `json:"images"`
|
EndDate string `json:"end_date"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Copyright string `json:"copyright"`
|
||||||
|
CopyrightLink string `json:"copyright_link"`
|
||||||
|
Resolution string `json:"resolution"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BingRecord struct {
|
type BingRecord struct {
|
||||||
@@ -30,8 +41,33 @@ type BingRecord struct {
|
|||||||
Copyright string `json:"copyright"`
|
Copyright string `json:"copyright"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Favorited bool `json:"favorited"`
|
Favorited bool `json:"favorited"`
|
||||||
|
Hidden bool `json:"hidden"`
|
||||||
|
Market string `json:"market"`
|
||||||
|
Source string `json:"source"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxBingRecords = 2000
|
||||||
|
|
||||||
|
var bingMarkets = []string{
|
||||||
|
"zh-CN", "en-US", "ja-JP", "de-DE", "fr-FR",
|
||||||
|
"en-GB", "ko-KR", "ru-RU", "pt-BR", "es-ES",
|
||||||
|
}
|
||||||
|
|
||||||
|
var bingMarketLabels = map[string]string{
|
||||||
|
"zh-CN": "中国",
|
||||||
|
"en-US": "美国",
|
||||||
|
"ja-JP": "日本",
|
||||||
|
"de-DE": "德国",
|
||||||
|
"fr-FR": "法国",
|
||||||
|
"en-GB": "英国",
|
||||||
|
"ko-KR": "韩国",
|
||||||
|
"ru-RU": "俄罗斯",
|
||||||
|
"pt-BR": "巴西",
|
||||||
|
"es-ES": "西班牙",
|
||||||
|
}
|
||||||
|
|
||||||
|
const bingBiturlAPI = "https://bing.biturl.top/?format=json"
|
||||||
|
|
||||||
type BingHistory struct {
|
type BingHistory struct {
|
||||||
Records []BingRecord `json:"records"`
|
Records []BingRecord `json:"records"`
|
||||||
CurrentIdx int `json:"currentIdx"`
|
CurrentIdx int `json:"currentIdx"`
|
||||||
@@ -60,91 +96,250 @@ func loadBingHistory() *BingHistory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func saveBingHistory(h *BingHistory) error {
|
func saveBingHistory(h *BingHistory) error {
|
||||||
data, _ := json.MarshalIndent(h, "", " ")
|
data, err := json.MarshalIndent(h, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Bing 历史序列化失败: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
return os.WriteFile(bingHistoryPath(), data, 0644)
|
return os.WriteFile(bingHistoryPath(), data, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bingKey builds a composite dedup key: market + "_" + date
|
||||||
|
func bingKey(market, date string) string {
|
||||||
|
if market == "" {
|
||||||
|
return "_" + date
|
||||||
|
}
|
||||||
|
return market + "_" + date
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadBingImage downloads a single Bing image from the record's URLBase
|
||||||
|
func downloadBingImage(r *BingRecord) error {
|
||||||
|
imgURL := r.URLBase
|
||||||
|
if imgURL == "" {
|
||||||
|
return fmt.Errorf("empty URLBase for %s", r.Filename)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(imgURL, "http") {
|
||||||
|
imgURL = "https://www.bing.com" + imgURL
|
||||||
|
}
|
||||||
|
// For official Bing source, ensure resolution suffix
|
||||||
|
if r.Source == "bing_official" || r.Source == "" {
|
||||||
|
if !strings.Contains(imgURL, "_1920x1080") && !strings.HasSuffix(imgURL, ".jpg") {
|
||||||
|
imgURL += "_1920x1080.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imgResp, err := httpClient.Get(imgURL)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("http get failed: %w", err)
|
||||||
|
}
|
||||||
|
defer imgResp.Body.Close()
|
||||||
|
imgData, err := io.ReadAll(imgResp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read body failed: %w", err)
|
||||||
|
}
|
||||||
|
if len(imgData) == 0 {
|
||||||
|
return fmt.Errorf("empty response body")
|
||||||
|
}
|
||||||
|
|
||||||
|
localPath := filepath.Join(bingDir(), r.Filename)
|
||||||
|
return os.WriteFile(localPath, imgData, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchBingHistory fetches Bing wallpapers from multiple markets and sources.
|
||||||
|
// Phase 1: Official Bing API per market (idx=0,8,...192)
|
||||||
|
// Phase 2: biturl.top supplement per market (5 attempts each)
|
||||||
|
// Phase 3: Enforce maxBingRecords cap
|
||||||
func fetchBingHistory() {
|
func fetchBingHistory() {
|
||||||
bingMu.Lock()
|
bingMu.Lock()
|
||||||
defer bingMu.Unlock()
|
defer bingMu.Unlock()
|
||||||
|
|
||||||
os.MkdirAll(bingDir(), 0755)
|
os.MkdirAll(bingDir(), 0755)
|
||||||
existing := loadBingHistory()
|
existing := loadBingHistory()
|
||||||
|
|
||||||
|
// Build composite-key map from existing records
|
||||||
existingMap := make(map[string]BingRecord)
|
existingMap := make(map[string]BingRecord)
|
||||||
for _, r := range existing.Records {
|
for _, r := range existing.Records {
|
||||||
existingMap[r.Date] = r
|
existingMap[bingKey(r.Market, r.Date)] = r
|
||||||
|
// Migration: old records with Market="" were from zh-CN single market
|
||||||
|
if r.Market == "" {
|
||||||
|
existingMap[bingKey("zh-CN", r.Date)] = r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页下载: idx=0,8,16... 直到命中已有记录或无新图片
|
cfg := loadConfig()
|
||||||
for idx := 0; idx < 80; idx += 8 {
|
markets := cfg.BingMarkets
|
||||||
url := fmt.Sprintf("%s&idx=%d", bingAPIBase, idx)
|
if len(markets) == 0 {
|
||||||
resp, err := httpClient.Get(url)
|
markets = bingMarkets
|
||||||
if err != nil {
|
}
|
||||||
log.Printf("Bing API 请求失败 (idx=%d): %v", idx, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
data, _ := io.ReadAll(resp.Body)
|
|
||||||
resp.Body.Close()
|
|
||||||
var br bingResponse
|
|
||||||
if json.Unmarshal(data, &br) != nil || len(br.Images) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
newCount := 0
|
// track new records per key for dedup across phases
|
||||||
for _, img := range br.Images {
|
newRecords := make(map[string]BingRecord)
|
||||||
date := img.StartDate
|
|
||||||
if date == "" {
|
// ---- Phase 1: Official Bing API per market ----
|
||||||
|
for _, market := range markets {
|
||||||
|
for idx := 0; idx < 192; idx += 8 {
|
||||||
|
url := fmt.Sprintf("%s&mkt=%s&idx=%d", bingAPIPrefix, market, idx)
|
||||||
|
resp, err := httpClient.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Bing API 请求失败 (market=%s, idx=%d): %v", market, idx, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
data, _ := io.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
var br bingResponse
|
||||||
|
if json.Unmarshal(data, &br) != nil || len(br.Images) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
pageNew := 0
|
||||||
|
for _, img := range br.Images {
|
||||||
|
date := img.StartDate
|
||||||
|
if date == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := bingKey(market, date)
|
||||||
|
if _, exists := existingMap[key]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := newRecords[key]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the image
|
||||||
|
imgURL := img.URL
|
||||||
|
if !strings.HasPrefix(imgURL, "http") {
|
||||||
|
imgURL = "https://www.bing.com" + imgURL
|
||||||
|
}
|
||||||
|
|
||||||
|
imgResp, err := httpClient.Get(imgURL)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
imgData, _ := io.ReadAll(imgResp.Body)
|
||||||
|
imgResp.Body.Close()
|
||||||
|
if len(imgData) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := fmt.Sprintf("%s_%s.jpg", market, date)
|
||||||
|
localPath := filepath.Join(bingDir(), filename)
|
||||||
|
os.WriteFile(localPath, imgData, 0644)
|
||||||
|
log.Printf("Bing 壁纸已下载: %s (%d bytes)", filename, len(imgData))
|
||||||
|
|
||||||
|
newRecords[key] = BingRecord{
|
||||||
|
Date: date,
|
||||||
|
URLBase: img.URLBase,
|
||||||
|
Copyright: img.Copyright,
|
||||||
|
Filename: filename,
|
||||||
|
Market: market,
|
||||||
|
Source: "bing_official",
|
||||||
|
}
|
||||||
|
pageNew++
|
||||||
|
}
|
||||||
|
|
||||||
|
if pageNew == 0 && idx > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Phase 2: biturl.top supplement ----
|
||||||
|
for _, market := range markets {
|
||||||
|
for attempt := 0; attempt < 5; attempt++ {
|
||||||
|
url := fmt.Sprintf("%s&mkt=%s", bingBiturlAPI, market)
|
||||||
|
resp, err := httpClient.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Bing biturl 请求失败 (market=%s): %v", market, err)
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, exists := existingMap[date]; exists {
|
data, _ := io.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
var bit biturlResponse
|
||||||
|
if json.Unmarshal(data, &bit) != nil || bit.StartDate == "" {
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
imgURL := img.URL
|
date := bit.StartDate
|
||||||
if !strings.HasPrefix(imgURL, "http") {
|
key := bingKey(market, date)
|
||||||
imgURL = "https://www.bing.com" + imgURL
|
if _, exists := existingMap[key]; exists {
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := newRecords[key]; exists {
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Download from biturl URL
|
||||||
|
imgURL := bit.URL
|
||||||
imgResp, err := httpClient.Get(imgURL)
|
imgResp, err := httpClient.Get(imgURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
imgData, _ := io.ReadAll(imgResp.Body)
|
imgData, _ := io.ReadAll(imgResp.Body)
|
||||||
imgResp.Body.Close()
|
imgResp.Body.Close()
|
||||||
if len(imgData) == 0 {
|
if len(imgData) == 0 {
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
filename := date + ".jpg"
|
filename := fmt.Sprintf("%s_%s.jpg", market, date)
|
||||||
localPath := filepath.Join(bingDir(), filename)
|
localPath := filepath.Join(bingDir(), filename)
|
||||||
os.WriteFile(localPath, imgData, 0644)
|
os.WriteFile(localPath, imgData, 0644)
|
||||||
log.Printf("Bing 壁纸已下载: %s (%d bytes)", filename, len(imgData))
|
log.Printf("Bing biturl 壁纸已下载: %s (%d bytes)", filename, len(imgData))
|
||||||
|
|
||||||
existingMap[date] = BingRecord{
|
newRecords[key] = BingRecord{
|
||||||
Date: date,
|
Date: date,
|
||||||
URLBase: img.URLBase,
|
URLBase: bit.URL,
|
||||||
Copyright: img.Copyright,
|
Copyright: bit.Copyright,
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
|
Market: market,
|
||||||
|
Source: "bing_biturl",
|
||||||
}
|
}
|
||||||
newCount++
|
time.Sleep(300 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
if newCount == 0 && idx > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按 API 返回顺序重建 records (newest first)
|
// Merge existing + new records
|
||||||
// 用 existingMap 里所有记录按 date 降序排列
|
allMap := make(map[string]BingRecord)
|
||||||
|
for k, r := range existingMap {
|
||||||
|
allMap[k] = r
|
||||||
|
}
|
||||||
|
for k, r := range newRecords {
|
||||||
|
allMap[k] = r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by date ascending (oldest first), so index 0 = oldest, index N = newest
|
||||||
|
// Left-to-right navigation: left = older (smaller idx), right = newer (larger idx)
|
||||||
var records []BingRecord
|
var records []BingRecord
|
||||||
for _, r := range existingMap {
|
for _, r := range allMap {
|
||||||
records = append(records, r)
|
records = append(records, r)
|
||||||
}
|
}
|
||||||
sort.Slice(records, func(i, j int) bool {
|
sort.Slice(records, func(i, j int) bool {
|
||||||
return records[i].Date > records[j].Date
|
if records[i].Date != records[j].Date {
|
||||||
|
return records[i].Date < records[j].Date
|
||||||
|
}
|
||||||
|
return records[i].Market < records[j].Market
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ---- Phase 3: Enforce 2000 cap ----
|
||||||
|
if len(records) > maxBingRecords {
|
||||||
|
// Records sorted oldest-first, so oldest are at the head
|
||||||
|
toRemove := records[:len(records)-maxBingRecords]
|
||||||
|
records = records[len(records)-maxBingRecords:]
|
||||||
|
for _, r := range toRemove {
|
||||||
|
p := filepath.Join(bingDir(), r.Filename)
|
||||||
|
os.Remove(p)
|
||||||
|
}
|
||||||
|
log.Printf("Bing 壁纸: 裁剪到 %d 张, 删除 %d 张旧图", maxBingRecords, len(toRemove))
|
||||||
|
}
|
||||||
|
|
||||||
history := &BingHistory{
|
history := &BingHistory{
|
||||||
Records: records,
|
Records: records,
|
||||||
CurrentIdx: 0,
|
CurrentIdx: 0,
|
||||||
@@ -154,7 +349,7 @@ func fetchBingHistory() {
|
|||||||
}
|
}
|
||||||
saveBingHistory(history)
|
saveBingHistory(history)
|
||||||
|
|
||||||
log.Printf("Bing 壁纸: 共 %d 张", len(records))
|
log.Printf("Bing 壁纸: 共 %d 张 (新增 %d 张)", len(records), len(newRecords))
|
||||||
reloadWallpaper()
|
reloadWallpaper()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +358,18 @@ func getCurrentBingPath() string {
|
|||||||
if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) {
|
if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return filepath.Join(bingDir(), h.Records[h.CurrentIdx].Filename)
|
r := &h.Records[h.CurrentIdx]
|
||||||
|
p := filepath.Join(bingDir(), r.Filename)
|
||||||
|
if _, err := os.Stat(p); err == nil {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
// File missing, try re-download
|
||||||
|
log.Printf("Bing 壁纸文件缺失, 尝试重新下载: %s", r.Filename)
|
||||||
|
if err := downloadBingImage(r); err != nil {
|
||||||
|
log.Printf("Bing 重新下载失败: %s, %v", r.Filename, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentBingRecord() *BingRecord {
|
func getCurrentBingRecord() *BingRecord {
|
||||||
@@ -174,6 +380,8 @@ func getCurrentBingRecord() *BingRecord {
|
|||||||
return &h.Records[h.CurrentIdx]
|
return &h.Records[h.CurrentIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bingPrev moves to the previous (older) image, skipping hidden ones.
|
||||||
|
// Records sorted oldest-first, so prev = decrement index.
|
||||||
func bingPrev() {
|
func bingPrev() {
|
||||||
bingMu.Lock()
|
bingMu.Lock()
|
||||||
defer bingMu.Unlock()
|
defer bingMu.Unlock()
|
||||||
@@ -182,12 +390,24 @@ func bingPrev() {
|
|||||||
if len(h.Records) == 0 {
|
if len(h.Records) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.CurrentIdx = (h.CurrentIdx + 1) % len(h.Records)
|
attempts := 0
|
||||||
|
for {
|
||||||
|
h.CurrentIdx = (h.CurrentIdx - 1 + len(h.Records)) % len(h.Records)
|
||||||
|
if !h.Records[h.CurrentIdx].Hidden {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attempts++
|
||||||
|
if attempts >= len(h.Records) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
saveBingHistory(h)
|
saveBingHistory(h)
|
||||||
log.Printf("Bing 壁纸: 上一个 (idx=%d, date=%s)", h.CurrentIdx, h.Records[h.CurrentIdx].Date)
|
log.Printf("Bing 壁纸: 上一个 (idx=%d, date=%s)", h.CurrentIdx, h.Records[h.CurrentIdx].Date)
|
||||||
bingReloadImage()
|
bingReloadImage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bingNext moves to the next (newer) image, skipping hidden ones.
|
||||||
|
// Records sorted oldest-first, so next = increment index.
|
||||||
func bingNext() {
|
func bingNext() {
|
||||||
bingMu.Lock()
|
bingMu.Lock()
|
||||||
defer bingMu.Unlock()
|
defer bingMu.Unlock()
|
||||||
@@ -196,7 +416,17 @@ func bingNext() {
|
|||||||
if len(h.Records) == 0 {
|
if len(h.Records) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h.CurrentIdx = (h.CurrentIdx - 1 + len(h.Records)) % len(h.Records)
|
attempts := 0
|
||||||
|
for {
|
||||||
|
h.CurrentIdx = (h.CurrentIdx + 1) % len(h.Records)
|
||||||
|
if !h.Records[h.CurrentIdx].Hidden {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attempts++
|
||||||
|
if attempts >= len(h.Records) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
saveBingHistory(h)
|
saveBingHistory(h)
|
||||||
log.Printf("Bing 壁纸: 下一个 (idx=%d, date=%s)", h.CurrentIdx, h.Records[h.CurrentIdx].Date)
|
log.Printf("Bing 壁纸: 下一个 (idx=%d, date=%s)", h.CurrentIdx, h.Records[h.CurrentIdx].Date)
|
||||||
bingReloadImage()
|
bingReloadImage()
|
||||||
@@ -221,10 +451,46 @@ func bingToggleFavorite() string {
|
|||||||
return "★ 收藏当前壁纸"
|
return "★ 收藏当前壁纸"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bingToggleDislike toggles the hidden flag on the current record.
|
||||||
|
// When hidden, auto-advances to the next visible image.
|
||||||
|
func bingToggleDislike() string {
|
||||||
|
bingMu.Lock()
|
||||||
|
defer bingMu.Unlock()
|
||||||
|
|
||||||
|
h := loadBingHistory()
|
||||||
|
if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) {
|
||||||
|
return bingCurrentState()
|
||||||
|
}
|
||||||
|
r := &h.Records[h.CurrentIdx]
|
||||||
|
r.Hidden = !r.Hidden
|
||||||
|
saveBingHistory(h)
|
||||||
|
|
||||||
|
if r.Hidden {
|
||||||
|
log.Printf("Bing 壁纸: 已隐藏 (date=%s)", r.Date)
|
||||||
|
// Advance to next visible (newer = +1)
|
||||||
|
attempts := 0
|
||||||
|
for {
|
||||||
|
h.CurrentIdx = (h.CurrentIdx + 1) % len(h.Records)
|
||||||
|
if !h.Records[h.CurrentIdx].Hidden {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attempts++
|
||||||
|
if attempts >= len(h.Records) {
|
||||||
|
return bingCurrentState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveBingHistory(h)
|
||||||
|
bingReloadImage()
|
||||||
|
return bingCurrentState()
|
||||||
|
}
|
||||||
|
log.Printf("Bing 壁纸: 已取消隐藏 (date=%s)", r.Date)
|
||||||
|
return bingCurrentState()
|
||||||
|
}
|
||||||
|
|
||||||
func bingCurrentState() string {
|
func bingCurrentState() string {
|
||||||
h := loadBingHistory()
|
h := loadBingHistory()
|
||||||
if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) {
|
if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) {
|
||||||
return `{"fav":false,"label":"☆","copyright":"","idx":0,"total":0}`
|
return `{"fav":false,"label":"☆","copyright":"","idx":0,"total":0,"hidden":false}`
|
||||||
}
|
}
|
||||||
r := h.Records[h.CurrentIdx]
|
r := h.Records[h.CurrentIdx]
|
||||||
label := "☆"
|
label := "☆"
|
||||||
@@ -239,6 +505,8 @@ func bingCurrentState() string {
|
|||||||
"filename": r.Filename,
|
"filename": r.Filename,
|
||||||
"idx": h.CurrentIdx,
|
"idx": h.CurrentIdx,
|
||||||
"total": len(h.Records),
|
"total": len(h.Records),
|
||||||
|
"hidden": r.Hidden,
|
||||||
|
"market": r.Market,
|
||||||
})
|
})
|
||||||
return string(data)
|
return string(data)
|
||||||
}
|
}
|
||||||
@@ -253,7 +521,7 @@ func bingFavoritesJSON() string {
|
|||||||
}
|
}
|
||||||
var favs []favItem
|
var favs []favItem
|
||||||
for i, r := range h.Records {
|
for i, r := range h.Records {
|
||||||
if r.Favorited {
|
if r.Favorited && !r.Hidden {
|
||||||
favs = append(favs, favItem{r.Date, r.Copyright, r.Filename, i})
|
favs = append(favs, favItem{r.Date, r.Copyright, r.Filename, i})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,6 +547,18 @@ func bingSetByIdx(idx int) string {
|
|||||||
|
|
||||||
func bingThumbDataURI(filename string) string {
|
func bingThumbDataURI(filename string) string {
|
||||||
p := filepath.Join(bingDir(), filename)
|
p := filepath.Join(bingDir(), filename)
|
||||||
|
// Auto redownload if missing
|
||||||
|
if _, err := os.Stat(p); err != nil {
|
||||||
|
// Try to find the record and re-download
|
||||||
|
h := loadBingHistory()
|
||||||
|
for _, r := range h.Records {
|
||||||
|
if r.Filename == filename {
|
||||||
|
log.Printf("Bing 缩略图文件缺失, 尝试重新下载: %s", filename)
|
||||||
|
downloadBingImage(&r)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return imageToDataURI(p)
|
return imageToDataURI(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +573,7 @@ func bingWallpaperLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定时切换壁纸 (1 小时间隔)
|
// Auto-refresh: cycle through images (newest direction)
|
||||||
go func() {
|
go func() {
|
||||||
ticker := time.NewTicker(1 * time.Hour)
|
ticker := time.NewTicker(1 * time.Hour)
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
@@ -307,7 +587,18 @@ func bingWallpaperLoop() {
|
|||||||
bingMu.Unlock()
|
bingMu.Unlock()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
h.CurrentIdx = (h.CurrentIdx + 1) % len(h.Records)
|
// Move to next (newer) image, skip hidden
|
||||||
|
attempts := 0
|
||||||
|
for {
|
||||||
|
h.CurrentIdx = (h.CurrentIdx + 1) % len(h.Records)
|
||||||
|
if !h.Records[h.CurrentIdx].Hidden {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attempts++
|
||||||
|
if attempts >= len(h.Records) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
saveBingHistory(h)
|
saveBingHistory(h)
|
||||||
bingMu.Unlock()
|
bingMu.Unlock()
|
||||||
bingReloadImage()
|
bingReloadImage()
|
||||||
@@ -315,6 +606,7 @@ func bingWallpaperLoop() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Periodic fetch
|
||||||
go func() {
|
go func() {
|
||||||
fetchTicker := time.NewTicker(4 * time.Hour)
|
fetchTicker := time.NewTicker(4 * time.Hour)
|
||||||
for range fetchTicker.C {
|
for range fetchTicker.C {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ type Config struct {
|
|||||||
HideKnowledge bool `json:"hideKnowledge"`
|
HideKnowledge bool `json:"hideKnowledge"`
|
||||||
SavedColors []SavedColor `json:"savedColors"`
|
SavedColors []SavedColor `json:"savedColors"`
|
||||||
BingAutoRefresh bool `json:"bingAutoRefresh"`
|
BingAutoRefresh bool `json:"bingAutoRefresh"`
|
||||||
|
BingMarkets []string `json:"bingMarkets,omitempty"`
|
||||||
AutoStart bool `json:"autoStart"`
|
AutoStart bool `json:"autoStart"`
|
||||||
QWeatherKey string `json:"qweatherKey,omitempty"`
|
QWeatherKey string `json:"qweatherKey,omitempty"`
|
||||||
TianapiKey string `json:"tianapiKey,omitempty"`
|
TianapiKey string `json:"tianapiKey,omitempty"`
|
||||||
@@ -116,6 +117,9 @@ func loadConfig() *Config {
|
|||||||
if cfg.Layout == "" {
|
if cfg.Layout == "" {
|
||||||
cfg.Layout = LayoutSingle
|
cfg.Layout = LayoutSingle
|
||||||
}
|
}
|
||||||
|
if len(cfg.BingMarkets) == 0 {
|
||||||
|
cfg.BingMarkets = []string{"zh-CN", "en-US", "ja-JP", "de-DE", "fr-FR", "en-GB", "ko-KR", "ru-RU", "pt-BR", "es-ES"}
|
||||||
|
}
|
||||||
return &cfg
|
return &cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$csvPath = Join-Path $PSScriptRoot "china-cities.csv"
|
||||||
|
if (-not (Test-Path $csvPath)) {
|
||||||
|
Write-Host "下载城市数据 CSV..."
|
||||||
|
$urls = @(
|
||||||
|
"https://raw.githubusercontent.com/qwd/LocationList/master/China-City-List-latest.csv",
|
||||||
|
"https://cdn.jsdelivr.net/gh/qwd/LocationList@master/China-City-List-latest.csv"
|
||||||
|
)
|
||||||
|
$ok = $false
|
||||||
|
foreach ($u in $urls) {
|
||||||
|
try { Invoke-WebRequest -Uri $u -OutFile $csvPath -UseBasicParsing; Write-Host "OK: $u"; $ok = $true; break }
|
||||||
|
catch { Write-Host "失败: $($_.Exception.Message)" }
|
||||||
|
}
|
||||||
|
if (-not $ok) { Write-Host "CSV 下载失败"; exit 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
$provMap = @{
|
||||||
|
"北京市"="北京"; "天津市"="天津"; "上海市"="上海"; "重庆市"="重庆";
|
||||||
|
"河北省"="河北"; "山西省"="山西"; "辽宁省"="辽宁"; "吉林省"="吉林"; "黑龙江省"="黑龙江";
|
||||||
|
"江苏省"="江苏"; "浙江省"="浙江"; "安徽省"="安徽"; "福建省"="福建"; "江西省"="江西";
|
||||||
|
"山东省"="山东"; "河南省"="河南"; "湖北省"="湖北"; "湖南省"="湖南"; "广东省"="广东";
|
||||||
|
"海南省"="海南"; "四川省"="四川"; "贵州省"="贵州"; "云南省"="云南"; "陕西省"="陕西";
|
||||||
|
"甘肃省"="甘肃"; "青海省"="青海";
|
||||||
|
"内蒙古自治区"="内蒙古"; "广西壮族自治区"="广西"; "西藏自治区"="西藏"; "宁夏回族自治区"="宁夏"; "新疆维吾尔自治区"="新疆";
|
||||||
|
"香港特别行政区"="香港"; "澳门特别行政区"="澳门"; "台湾省"="台湾";
|
||||||
|
}
|
||||||
|
$fullMap = @{}
|
||||||
|
foreach ($k in $provMap.Keys) { $fullMap[$provMap[$k]] = $k }
|
||||||
|
|
||||||
|
$provOrder = @(
|
||||||
|
"北京","天津","上海","重庆",
|
||||||
|
"河北","山西","辽宁","吉林","黑龙江",
|
||||||
|
"江苏","浙江","安徽","福建","江西","山东",
|
||||||
|
"河南","湖北","湖南","广东","海南",
|
||||||
|
"四川","贵州","云南","陕西","甘肃","青海",
|
||||||
|
"内蒙古","广西","西藏","宁夏","新疆",
|
||||||
|
"香港","澳门","台湾"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 解析 CSV(跳过第1行版本号)
|
||||||
|
$lines = Get-Content $csvPath -Encoding UTF8
|
||||||
|
$tempCsv = Join-Path $PSScriptRoot "_cities_temp.csv"
|
||||||
|
$lines[1..($lines.Length-1)] | Set-Content $tempCsv -Encoding UTF8
|
||||||
|
$csv = Import-Csv $tempCsv -Encoding UTF8
|
||||||
|
Remove-Item $tempCsv -Force
|
||||||
|
Write-Host "CSV 记录: $($csv.Count)"
|
||||||
|
|
||||||
|
# 按 Adm1 一次性分桶
|
||||||
|
$byAdm1 = @{}
|
||||||
|
foreach ($r in $csv) {
|
||||||
|
$a = $r.'Adm1_Name_ZH'
|
||||||
|
if (-not $byAdm1.ContainsKey($a)) { $byAdm1[$a] = New-Object System.Collections.ArrayList }
|
||||||
|
[void]$byAdm1[$a].Add($r)
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = New-Object System.Collections.ArrayList
|
||||||
|
$weird = New-Object System.Collections.ArrayList
|
||||||
|
|
||||||
|
foreach ($provShort in $provOrder) {
|
||||||
|
$provFull = $fullMap[$provShort]
|
||||||
|
$rows = @()
|
||||||
|
if ($byAdm1.ContainsKey($provFull)) { $rows = @($byAdm1[$provFull]) }
|
||||||
|
if ($rows.Count -eq 0) {
|
||||||
|
foreach ($k in $byAdm1.Keys) { if ($k -like "*$provShort*") { $rows += @($byAdm1[$k]) } }
|
||||||
|
}
|
||||||
|
if ($rows.Count -eq 0) { Write-Host "省份未找到: $provShort"; continue }
|
||||||
|
# 按 Adm2 分组,每 Adm2 取 Location_ID 最小(地级市中心)
|
||||||
|
$adm2Groups = $rows | Group-Object 'Adm2_Name_ZH'
|
||||||
|
$cnt = 0
|
||||||
|
foreach ($g in $adm2Groups) {
|
||||||
|
if ($g.Name -match "省直辖|直辖县|县级行政区") {
|
||||||
|
[void]$weird.Add("$provShort/$($g.Name)"); continue
|
||||||
|
}
|
||||||
|
$center = $g.Group | Sort-Object Location_ID | Select-Object -First 1
|
||||||
|
# 过滤非数字 ID(台湾乡镇异常十六进制 ID,非和风标准 9 位)
|
||||||
|
if ($center.Location_ID -notmatch '^\d+$') {
|
||||||
|
[void]$weird.Add("$provShort/$($g.Name)/$($center.Location_ID)"); continue
|
||||||
|
}
|
||||||
|
# name 用 Adm2 名去末尾"市"/"特别行政区"(避免区名如"双桥",简化港澳)
|
||||||
|
$name = $g.Name -replace '市$','' -replace '特别行政区$',''
|
||||||
|
[void]$result.Add([pscustomobject]@{ id=$center.Location_ID; name=$name; adm1=$provShort })
|
||||||
|
$cnt++
|
||||||
|
}
|
||||||
|
Write-Host (" {0,-8} : {1} 城" -f $provShort, $cnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "总计: $($result.Count) 城"
|
||||||
|
if ($weird.Count -gt 0) { Write-Host "已过滤 ($($weird.Count)): $($weird -join '; ')" }
|
||||||
|
|
||||||
|
# 输出
|
||||||
|
$utf8 = [System.Text.UTF8Encoding]::new($false)
|
||||||
|
[System.IO.File]::WriteAllText((Join-Path $PSScriptRoot "cities.json"), ($result | ConvertTo-Json -Depth 2), $utf8)
|
||||||
|
|
||||||
|
$goLines = $result | ForEach-Object { ' {"' + $_.id + '", "' + $_.name + '", "' + $_.name + '", "' + $_.adm1 + '"},' }
|
||||||
|
$goBlock = "var cities = []City{`n" + ($goLines -join "`n") + "`n}"
|
||||||
|
[System.IO.File]::WriteAllText((Join-Path $PSScriptRoot "cities.go.txt"), $goBlock, $utf8)
|
||||||
|
|
||||||
|
$vueLines = $result | ForEach-Object { " { id: '" + $_.id + "', name: '" + $_.name + "', province: '" + $_.adm1 + "' }," }
|
||||||
|
$vueBlock = "const allCities = [`n" + ($vueLines -join "`n") + "`n].map((c, i) => ({ ...c, _idx: i }))"
|
||||||
|
[System.IO.File]::WriteAllText((Join-Path $PSScriptRoot "cities.vue.txt"), $vueBlock, $utf8)
|
||||||
|
|
||||||
|
Write-Host "已生成: cities.json / cities.go.txt / cities.vue.txt"
|
||||||
+17
@@ -390,6 +390,23 @@ func openSettingsWindow() {
|
|||||||
w.Bind("bingThumbDataURI", func(filename string) string {
|
w.Bind("bingThumbDataURI", func(filename string) string {
|
||||||
return bingThumbDataURI(filename)
|
return bingThumbDataURI(filename)
|
||||||
})
|
})
|
||||||
|
w.Bind("bingToggleDislike", func() string {
|
||||||
|
return bingToggleDislike()
|
||||||
|
})
|
||||||
|
w.Bind("getBingMarkets", func() string {
|
||||||
|
cfg := loadConfig()
|
||||||
|
data, _ := json.Marshal(cfg.BingMarkets)
|
||||||
|
return string(data)
|
||||||
|
})
|
||||||
|
w.Bind("saveBingMarkets", func(marketsJSON string) string {
|
||||||
|
var markets []string
|
||||||
|
if json.Unmarshal([]byte(marketsJSON), &markets) == nil {
|
||||||
|
cfg := loadConfig()
|
||||||
|
cfg.BingMarkets = markets
|
||||||
|
saveConfig(cfg)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
})
|
||||||
|
|
||||||
w.Bind("pickSolidColor", func() string {
|
w.Bind("pickSolidColor", func() string {
|
||||||
hwnd := uintptr(w.Window())
|
hwnd := uintptr(w.Window())
|
||||||
|
|||||||
+382
-14
@@ -23,23 +23,391 @@ type City struct {
|
|||||||
|
|
||||||
var cities = []City{
|
var cities = []City{
|
||||||
{"101010100", "北京", "北京", "北京"},
|
{"101010100", "北京", "北京", "北京"},
|
||||||
|
{"101030100", "天津", "天津", "天津"},
|
||||||
{"101020100", "上海", "上海", "上海"},
|
{"101020100", "上海", "上海", "上海"},
|
||||||
{"101280101", "广州", "广州", "广东"},
|
{"101040100", "重庆", "重庆", "重庆"},
|
||||||
{"101280601", "深圳", "深圳", "广东"},
|
|
||||||
{"101200101", "武汉", "武汉", "湖北"},
|
|
||||||
{"101110101", "西安", "西安", "陕西"},
|
|
||||||
{"101210101", "杭州", "杭州", "浙江"},
|
|
||||||
{"101190101", "南京", "南京", "江苏"},
|
|
||||||
{"101070101", "沈阳", "沈阳", "辽宁"},
|
|
||||||
{"101050101", "哈尔滨", "哈尔滨", "黑龙江"},
|
|
||||||
{"101250101", "长沙", "长沙", "湖南"},
|
|
||||||
{"101270101", "成都", "成都", "四川"},
|
|
||||||
{"101090101", "石家庄", "石家庄", "河北"},
|
{"101090101", "石家庄", "石家庄", "河北"},
|
||||||
{"101090206", "任丘", "任丘", "河北"},
|
{"101090201", "保定", "保定", "河北"},
|
||||||
{"101090301", "邯郸", "邯郸", "河北"},
|
{"101090301", "张家口", "张家口", "河北"},
|
||||||
{"101290106", "宣威", "宣威", "云南"},
|
{"101090401", "承德", "承德", "河北"},
|
||||||
{"101290101", "昆明", "昆明", "云南"},
|
{"101090501", "唐山", "唐山", "河北"},
|
||||||
|
{"101090601", "廊坊", "廊坊", "河北"},
|
||||||
|
{"101090701", "沧州", "沧州", "河北"},
|
||||||
|
{"101090801", "衡水", "衡水", "河北"},
|
||||||
|
{"101090901", "邢台", "邢台", "河北"},
|
||||||
|
{"101091001", "邯郸", "邯郸", "河北"},
|
||||||
|
{"101091101", "秦皇岛", "秦皇岛", "河北"},
|
||||||
|
{"101091201", "雄安新区", "雄安新区", "河北"},
|
||||||
|
{"101100101", "太原", "太原", "山西"},
|
||||||
|
{"101100201", "大同", "大同", "山西"},
|
||||||
|
{"101100301", "阳泉", "阳泉", "山西"},
|
||||||
|
{"101100401", "晋中", "晋中", "山西"},
|
||||||
|
{"101100501", "长治", "长治", "山西"},
|
||||||
|
{"101100601", "晋城", "晋城", "山西"},
|
||||||
|
{"101100701", "临汾", "临汾", "山西"},
|
||||||
|
{"101100801", "运城", "运城", "山西"},
|
||||||
|
{"101100901", "朔州", "朔州", "山西"},
|
||||||
|
{"101101001", "忻州", "忻州", "山西"},
|
||||||
|
{"101101100", "吕梁", "吕梁", "山西"},
|
||||||
|
{"101070101", "沈阳", "沈阳", "辽宁"},
|
||||||
|
{"101070201", "大连", "大连", "辽宁"},
|
||||||
|
{"101070301", "鞍山", "鞍山", "辽宁"},
|
||||||
|
{"101070401", "抚顺", "抚顺", "辽宁"},
|
||||||
|
{"101070501", "本溪", "本溪", "辽宁"},
|
||||||
|
{"101070601", "丹东", "丹东", "辽宁"},
|
||||||
|
{"101070701", "锦州", "锦州", "辽宁"},
|
||||||
|
{"101070801", "营口", "营口", "辽宁"},
|
||||||
|
{"101070901", "阜新", "阜新", "辽宁"},
|
||||||
|
{"101071001", "辽阳", "辽阳", "辽宁"},
|
||||||
|
{"101071101", "铁岭", "铁岭", "辽宁"},
|
||||||
|
{"101071201", "朝阳", "朝阳", "辽宁"},
|
||||||
|
{"101071301", "盘锦", "盘锦", "辽宁"},
|
||||||
|
{"101071401", "葫芦岛", "葫芦岛", "辽宁"},
|
||||||
|
{"101060101", "长春", "长春", "吉林"},
|
||||||
|
{"101060201", "吉林", "吉林", "吉林"},
|
||||||
|
{"101060301", "延边朝鲜族自治州", "延边朝鲜族自治州", "吉林"},
|
||||||
|
{"101060401", "四平", "四平", "吉林"},
|
||||||
|
{"101060501", "通化", "通化", "吉林"},
|
||||||
|
{"101060601", "白城", "白城", "吉林"},
|
||||||
|
{"101060701", "辽源", "辽源", "吉林"},
|
||||||
|
{"101060801", "松原", "松原", "吉林"},
|
||||||
|
{"101060901", "白山", "白山", "吉林"},
|
||||||
|
{"101050101", "哈尔滨", "哈尔滨", "黑龙江"},
|
||||||
|
{"101050201", "齐齐哈尔", "齐齐哈尔", "黑龙江"},
|
||||||
|
{"101050301", "牡丹江", "牡丹江", "黑龙江"},
|
||||||
|
{"101050401", "佳木斯", "佳木斯", "黑龙江"},
|
||||||
|
{"101050501", "绥化", "绥化", "黑龙江"},
|
||||||
|
{"101050601", "黑河", "黑河", "黑龙江"},
|
||||||
|
{"101050701", "大兴安岭地区", "大兴安岭地区", "黑龙江"},
|
||||||
|
{"101050801", "伊春", "伊春", "黑龙江"},
|
||||||
|
{"101050901", "大庆", "大庆", "黑龙江"},
|
||||||
|
{"101051001", "七台河", "七台河", "黑龙江"},
|
||||||
|
{"101051101", "鸡西", "鸡西", "黑龙江"},
|
||||||
|
{"101051201", "鹤岗", "鹤岗", "黑龙江"},
|
||||||
|
{"101051301", "双鸭山", "双鸭山", "黑龙江"},
|
||||||
|
{"101190101", "南京", "南京", "江苏"},
|
||||||
|
{"101190201", "无锡", "无锡", "江苏"},
|
||||||
|
{"101190301", "镇江", "镇江", "江苏"},
|
||||||
|
{"101190401", "苏州", "苏州", "江苏"},
|
||||||
|
{"101190501", "南通", "南通", "江苏"},
|
||||||
|
{"101190601", "扬州", "扬州", "江苏"},
|
||||||
|
{"101190701", "盐城", "盐城", "江苏"},
|
||||||
|
{"101190801", "徐州", "徐州", "江苏"},
|
||||||
|
{"101190901", "淮安", "淮安", "江苏"},
|
||||||
|
{"101191001", "连云港", "连云港", "江苏"},
|
||||||
|
{"101191101", "常州", "常州", "江苏"},
|
||||||
|
{"101191201", "泰州", "泰州", "江苏"},
|
||||||
|
{"101191301", "宿迁", "宿迁", "江苏"},
|
||||||
|
{"101210101", "杭州", "杭州", "浙江"},
|
||||||
|
{"101210201", "湖州", "湖州", "浙江"},
|
||||||
|
{"101210301", "嘉兴", "嘉兴", "浙江"},
|
||||||
|
{"101210401", "宁波", "宁波", "浙江"},
|
||||||
|
{"101210501", "绍兴", "绍兴", "浙江"},
|
||||||
|
{"101210601", "台州", "台州", "浙江"},
|
||||||
|
{"101210701", "温州", "温州", "浙江"},
|
||||||
|
{"101210801", "丽水", "丽水", "浙江"},
|
||||||
|
{"101210901", "金华", "金华", "浙江"},
|
||||||
|
{"101211001", "衢州", "衢州", "浙江"},
|
||||||
|
{"101211101", "舟山", "舟山", "浙江"},
|
||||||
|
{"101220101", "合肥", "合肥", "安徽"},
|
||||||
|
{"101220201", "蚌埠", "蚌埠", "安徽"},
|
||||||
|
{"101220301", "芜湖", "芜湖", "安徽"},
|
||||||
|
{"101220401", "淮南", "淮南", "安徽"},
|
||||||
|
{"101220501", "马鞍山", "马鞍山", "安徽"},
|
||||||
|
{"101220601", "安庆", "安庆", "安徽"},
|
||||||
|
{"101220701", "宿州", "宿州", "安徽"},
|
||||||
|
{"101220801", "阜阳", "阜阳", "安徽"},
|
||||||
|
{"101220901", "亳州", "亳州", "安徽"},
|
||||||
|
{"101221001", "黄山", "黄山", "安徽"},
|
||||||
|
{"101221101", "滁州", "滁州", "安徽"},
|
||||||
|
{"101221201", "淮北", "淮北", "安徽"},
|
||||||
|
{"101221301", "铜陵", "铜陵", "安徽"},
|
||||||
|
{"101221401", "宣城", "宣城", "安徽"},
|
||||||
|
{"101221501", "六安", "六安", "安徽"},
|
||||||
|
{"101221701", "池州", "池州", "安徽"},
|
||||||
|
{"101230101", "福州", "福州", "福建"},
|
||||||
|
{"101230201", "厦门", "厦门", "福建"},
|
||||||
|
{"101230301", "宁德", "宁德", "福建"},
|
||||||
|
{"101230401", "莆田", "莆田", "福建"},
|
||||||
|
{"101230501", "泉州", "泉州", "福建"},
|
||||||
|
{"101230601", "漳州", "漳州", "福建"},
|
||||||
|
{"101230701", "龙岩", "龙岩", "福建"},
|
||||||
|
{"101230801", "三明", "三明", "福建"},
|
||||||
|
{"101230901", "南平", "南平", "福建"},
|
||||||
|
{"101240101", "南昌", "南昌", "江西"},
|
||||||
|
{"101240201", "九江", "九江", "江西"},
|
||||||
|
{"101240301", "上饶", "上饶", "江西"},
|
||||||
|
{"101240401", "抚州", "抚州", "江西"},
|
||||||
|
{"101240501", "宜春", "宜春", "江西"},
|
||||||
|
{"101240601", "吉安", "吉安", "江西"},
|
||||||
|
{"101240701", "赣州", "赣州", "江西"},
|
||||||
|
{"101240801", "景德镇", "景德镇", "江西"},
|
||||||
|
{"101240901", "萍乡", "萍乡", "江西"},
|
||||||
|
{"101241001", "新余", "新余", "江西"},
|
||||||
|
{"101241101", "鹰潭", "鹰潭", "江西"},
|
||||||
|
{"101120101", "济南", "济南", "山东"},
|
||||||
|
{"101120201", "青岛", "青岛", "山东"},
|
||||||
|
{"101120301", "淄博", "淄博", "山东"},
|
||||||
|
{"101120401", "德州", "德州", "山东"},
|
||||||
|
{"101120501", "烟台", "烟台", "山东"},
|
||||||
|
{"101120601", "潍坊", "潍坊", "山东"},
|
||||||
|
{"101120701", "济宁", "济宁", "山东"},
|
||||||
|
{"101120801", "泰安", "泰安", "山东"},
|
||||||
|
{"101120901", "临沂", "临沂", "山东"},
|
||||||
|
{"101121001", "菏泽", "菏泽", "山东"},
|
||||||
|
{"101121101", "滨州", "滨州", "山东"},
|
||||||
|
{"101121201", "东营", "东营", "山东"},
|
||||||
|
{"101121301", "威海", "威海", "山东"},
|
||||||
|
{"101121401", "枣庄", "枣庄", "山东"},
|
||||||
|
{"101121501", "日照", "日照", "山东"},
|
||||||
|
{"101121701", "聊城", "聊城", "山东"},
|
||||||
|
{"101180101", "郑州", "郑州", "河南"},
|
||||||
|
{"101180201", "安阳", "安阳", "河南"},
|
||||||
|
{"101180301", "新乡", "新乡", "河南"},
|
||||||
|
{"101180401", "许昌", "许昌", "河南"},
|
||||||
|
{"101180501", "平顶山", "平顶山", "河南"},
|
||||||
|
{"101180601", "信阳", "信阳", "河南"},
|
||||||
|
{"101180701", "南阳", "南阳", "河南"},
|
||||||
|
{"101180801", "开封", "开封", "河南"},
|
||||||
|
{"101180901", "洛阳", "洛阳", "河南"},
|
||||||
|
{"101181001", "商丘", "商丘", "河南"},
|
||||||
|
{"101181101", "焦作", "焦作", "河南"},
|
||||||
|
{"101181201", "鹤壁", "鹤壁", "河南"},
|
||||||
|
{"101181301", "濮阳", "濮阳", "河南"},
|
||||||
|
{"101181401", "周口", "周口", "河南"},
|
||||||
|
{"101181501", "漯河", "漯河", "河南"},
|
||||||
|
{"101181601", "驻马店", "驻马店", "河南"},
|
||||||
|
{"101181701", "三门峡", "三门峡", "河南"},
|
||||||
|
{"101181801", "济源", "济源", "河南"},
|
||||||
|
{"101200101", "武汉", "武汉", "湖北"},
|
||||||
|
{"101200201", "襄阳", "襄阳", "湖北"},
|
||||||
|
{"101200301", "鄂州", "鄂州", "湖北"},
|
||||||
|
{"101200401", "孝感", "孝感", "湖北"},
|
||||||
|
{"101200501", "黄冈", "黄冈", "湖北"},
|
||||||
|
{"101200601", "黄石", "黄石", "湖北"},
|
||||||
|
{"101200701", "咸宁", "咸宁", "湖北"},
|
||||||
|
{"101200801", "荆州", "荆州", "湖北"},
|
||||||
|
{"101200901", "宜昌", "宜昌", "湖北"},
|
||||||
|
{"101201001", "恩施土家族苗族自治州", "恩施土家族苗族自治州", "湖北"},
|
||||||
|
{"101201101", "十堰", "十堰", "湖北"},
|
||||||
|
{"101201201", "神农架林区", "神农架林区", "湖北"},
|
||||||
|
{"101201301", "随州", "随州", "湖北"},
|
||||||
|
{"101201401", "荆门", "荆门", "湖北"},
|
||||||
|
{"101201501", "天门", "天门", "湖北"},
|
||||||
|
{"101201601", "仙桃", "仙桃", "湖北"},
|
||||||
|
{"101201701", "潜江", "潜江", "湖北"},
|
||||||
|
{"101250101", "长沙", "长沙", "湖南"},
|
||||||
|
{"101250201", "湘潭", "湘潭", "湖南"},
|
||||||
|
{"101250301", "株洲", "株洲", "湖南"},
|
||||||
|
{"101250401", "衡阳", "衡阳", "湖南"},
|
||||||
|
{"101250501", "郴州", "郴州", "湖南"},
|
||||||
|
{"101250601", "常德", "常德", "湖南"},
|
||||||
|
{"101250700", "益阳", "益阳", "湖南"},
|
||||||
|
{"101250801", "娄底", "娄底", "湖南"},
|
||||||
|
{"101250901", "邵阳", "邵阳", "湖南"},
|
||||||
|
{"101251001", "岳阳", "岳阳", "湖南"},
|
||||||
|
{"101251101", "张家界", "张家界", "湖南"},
|
||||||
|
{"101251201", "怀化", "怀化", "湖南"},
|
||||||
|
{"101251401", "永州", "永州", "湖南"},
|
||||||
|
{"101251501", "湘西土家族苗族自治州", "湘西土家族苗族自治州", "湖南"},
|
||||||
|
{"101280101", "广州", "广州", "广东"},
|
||||||
|
{"101280201", "韶关", "韶关", "广东"},
|
||||||
|
{"101280301", "惠州", "惠州", "广东"},
|
||||||
|
{"101280401", "梅州", "梅州", "广东"},
|
||||||
|
{"101280501", "汕头", "汕头", "广东"},
|
||||||
|
{"101280601", "深圳", "深圳", "广东"},
|
||||||
|
{"101280701", "珠海", "珠海", "广东"},
|
||||||
|
{"101280800", "佛山", "佛山", "广东"},
|
||||||
|
{"101280901", "肇庆", "肇庆", "广东"},
|
||||||
|
{"101281001", "湛江", "湛江", "广东"},
|
||||||
|
{"101281101", "江门", "江门", "广东"},
|
||||||
|
{"101281201", "河源", "河源", "广东"},
|
||||||
|
{"101281301", "清远", "清远", "广东"},
|
||||||
|
{"101281401", "云浮", "云浮", "广东"},
|
||||||
|
{"101281501", "潮州", "潮州", "广东"},
|
||||||
|
{"101281601", "东莞", "东莞", "广东"},
|
||||||
|
{"101281701", "中山", "中山", "广东"},
|
||||||
|
{"101281801", "阳江", "阳江", "广东"},
|
||||||
|
{"101281901", "揭阳", "揭阳", "广东"},
|
||||||
|
{"101282001", "茂名", "茂名", "广东"},
|
||||||
|
{"101282101", "汕尾", "汕尾", "广东"},
|
||||||
|
{"101310101", "海口", "海口", "海南"},
|
||||||
|
{"101310201", "三亚", "三亚", "海南"},
|
||||||
|
{"101310202", "东方", "东方", "海南"},
|
||||||
|
{"101310203", "临高县", "临高县", "海南"},
|
||||||
|
{"101310204", "澄迈县", "澄迈县", "海南"},
|
||||||
|
{"101310205", "儋州", "儋州", "海南"},
|
||||||
|
{"101310206", "昌江黎族自治县", "昌江黎族自治县", "海南"},
|
||||||
|
{"101310207", "白沙黎族自治县", "白沙黎族自治县", "海南"},
|
||||||
|
{"101310208", "琼中黎族苗族自治县", "琼中黎族苗族自治县", "海南"},
|
||||||
|
{"101310209", "定安县", "定安县", "海南"},
|
||||||
|
{"101310210", "屯昌县", "屯昌县", "海南"},
|
||||||
|
{"101310211", "琼海", "琼海", "海南"},
|
||||||
|
{"101310212", "文昌", "文昌", "海南"},
|
||||||
|
{"101310214", "保亭黎族苗族自治县", "保亭黎族苗族自治县", "海南"},
|
||||||
|
{"101310215", "万宁", "万宁", "海南"},
|
||||||
|
{"101310216", "陵水黎族自治县", "陵水黎族自治县", "海南"},
|
||||||
|
{"101310221", "乐东黎族自治县", "乐东黎族自治县", "海南"},
|
||||||
|
{"101310222", "五指山", "五指山", "海南"},
|
||||||
|
{"101310301", "三沙", "三沙", "海南"},
|
||||||
|
{"101270101", "成都", "成都", "四川"},
|
||||||
|
{"101270201", "攀枝花", "攀枝花", "四川"},
|
||||||
|
{"101270301", "自贡", "自贡", "四川"},
|
||||||
|
{"101270401", "绵阳", "绵阳", "四川"},
|
||||||
|
{"101270501", "南充", "南充", "四川"},
|
||||||
|
{"101270601", "达州", "达州", "四川"},
|
||||||
|
{"101270701", "遂宁", "遂宁", "四川"},
|
||||||
|
{"101270801", "广安", "广安", "四川"},
|
||||||
|
{"101270901", "巴中", "巴中", "四川"},
|
||||||
|
{"101271001", "泸州", "泸州", "四川"},
|
||||||
|
{"101271101", "宜宾", "宜宾", "四川"},
|
||||||
|
{"101271201", "内江", "内江", "四川"},
|
||||||
|
{"101271301", "资阳", "资阳", "四川"},
|
||||||
|
{"101271401", "乐山", "乐山", "四川"},
|
||||||
|
{"101271501", "眉山", "眉山", "四川"},
|
||||||
|
{"101271601", "凉山彝族自治州", "凉山彝族自治州", "四川"},
|
||||||
|
{"101271701", "雅安", "雅安", "四川"},
|
||||||
|
{"101271801", "甘孜藏族自治州", "甘孜藏族自治州", "四川"},
|
||||||
|
{"101271901", "阿坝藏族羌族自治州", "阿坝藏族羌族自治州", "四川"},
|
||||||
|
{"101272001", "德阳", "德阳", "四川"},
|
||||||
|
{"101272101", "广元", "广元", "四川"},
|
||||||
{"101260101", "贵阳", "贵阳", "贵州"},
|
{"101260101", "贵阳", "贵阳", "贵州"},
|
||||||
|
{"101260201", "遵义", "遵义", "贵州"},
|
||||||
|
{"101260301", "安顺", "安顺", "贵州"},
|
||||||
|
{"101260401", "黔南布依族苗族自治州", "黔南布依族苗族自治州", "贵州"},
|
||||||
|
{"101260501", "黔东南苗族侗族自治州", "黔东南苗族侗族自治州", "贵州"},
|
||||||
|
{"101260601", "铜仁", "铜仁", "贵州"},
|
||||||
|
{"101260701", "毕节", "毕节", "贵州"},
|
||||||
|
{"101260801", "六盘水", "六盘水", "贵州"},
|
||||||
|
{"101260901", "黔西南布依族苗族自治州", "黔西南布依族苗族自治州", "贵州"},
|
||||||
|
{"101290101", "昆明", "昆明", "云南"},
|
||||||
|
{"101290201", "大理白族自治州", "大理白族自治州", "云南"},
|
||||||
|
{"101290301", "红河哈尼族彝族自治州", "红河哈尼族彝族自治州", "云南"},
|
||||||
|
{"101290401", "曲靖", "曲靖", "云南"},
|
||||||
|
{"101290501", "保山", "保山", "云南"},
|
||||||
|
{"101290601", "文山壮族苗族自治州", "文山壮族苗族自治州", "云南"},
|
||||||
|
{"101290701", "玉溪", "玉溪", "云南"},
|
||||||
|
{"101290801", "楚雄彝族自治州", "楚雄彝族自治州", "云南"},
|
||||||
|
{"101290901", "普洱", "普洱", "云南"},
|
||||||
|
{"101291001", "昭通", "昭通", "云南"},
|
||||||
|
{"101291101", "临沧", "临沧", "云南"},
|
||||||
|
{"101291201", "怒江傈僳族自治州", "怒江傈僳族自治州", "云南"},
|
||||||
|
{"101291301", "迪庆藏族自治州", "迪庆藏族自治州", "云南"},
|
||||||
|
{"101291401", "丽江", "丽江", "云南"},
|
||||||
|
{"101291501", "德宏傣族景颇族自治州", "德宏傣族景颇族自治州", "云南"},
|
||||||
|
{"101291601", "西双版纳傣族自治州", "西双版纳傣族自治州", "云南"},
|
||||||
|
{"101110101", "西安", "西安", "陕西"},
|
||||||
|
{"101110200", "咸阳", "咸阳", "陕西"},
|
||||||
|
{"101110300", "延安", "延安", "陕西"},
|
||||||
|
{"101110401", "榆林", "榆林", "陕西"},
|
||||||
|
{"101110501", "渭南", "渭南", "陕西"},
|
||||||
|
{"101110601", "商洛", "商洛", "陕西"},
|
||||||
|
{"101110701", "安康", "安康", "陕西"},
|
||||||
|
{"101110801", "汉中", "汉中", "陕西"},
|
||||||
|
{"101110901", "宝鸡", "宝鸡", "陕西"},
|
||||||
|
{"101111001", "铜川", "铜川", "陕西"},
|
||||||
|
{"101160101", "兰州", "兰州", "甘肃"},
|
||||||
|
{"101160201", "定西", "定西", "甘肃"},
|
||||||
|
{"101160301", "平凉", "平凉", "甘肃"},
|
||||||
|
{"101160401", "庆阳", "庆阳", "甘肃"},
|
||||||
|
{"101160501", "武威", "武威", "甘肃"},
|
||||||
|
{"101160601", "金昌", "金昌", "甘肃"},
|
||||||
|
{"101160701", "张掖", "张掖", "甘肃"},
|
||||||
|
{"101160801", "酒泉", "酒泉", "甘肃"},
|
||||||
|
{"101160901", "天水", "天水", "甘肃"},
|
||||||
|
{"101161001", "陇南", "陇南", "甘肃"},
|
||||||
|
{"101161101", "临夏回族自治州", "临夏回族自治州", "甘肃"},
|
||||||
|
{"101161201", "甘南藏族自治州", "甘南藏族自治州", "甘肃"},
|
||||||
|
{"101161301", "白银", "白银", "甘肃"},
|
||||||
|
{"101161401", "嘉峪关", "嘉峪关", "甘肃"},
|
||||||
|
{"101150101", "西宁", "西宁", "青海"},
|
||||||
|
{"101150201", "海东", "海东", "青海"},
|
||||||
|
{"101150301", "黄南藏族自治州", "黄南藏族自治州", "青海"},
|
||||||
|
{"101150401", "海南藏族自治州", "海南藏族自治州", "青海"},
|
||||||
|
{"101150501", "果洛藏族自治州", "果洛藏族自治州", "青海"},
|
||||||
|
{"101150601", "玉树藏族自治州", "玉树藏族自治州", "青海"},
|
||||||
|
{"101150701", "海西蒙古族藏族自治州", "海西蒙古族藏族自治州", "青海"},
|
||||||
|
{"101150801", "海北藏族自治州", "海北藏族自治州", "青海"},
|
||||||
|
{"101080101", "呼和浩特", "呼和浩特", "内蒙古"},
|
||||||
|
{"101080201", "包头", "包头", "内蒙古"},
|
||||||
|
{"101080301", "乌海", "乌海", "内蒙古"},
|
||||||
|
{"101080401", "乌兰察布", "乌兰察布", "内蒙古"},
|
||||||
|
{"101080501", "通辽", "通辽", "内蒙古"},
|
||||||
|
{"101080601", "赤峰", "赤峰", "内蒙古"},
|
||||||
|
{"101080701", "鄂尔多斯", "鄂尔多斯", "内蒙古"},
|
||||||
|
{"101080801", "巴彦淖尔", "巴彦淖尔", "内蒙古"},
|
||||||
|
{"101080901", "锡林郭勒盟", "锡林郭勒盟", "内蒙古"},
|
||||||
|
{"101081001", "呼伦贝尔", "呼伦贝尔", "内蒙古"},
|
||||||
|
{"101081101", "兴安盟", "兴安盟", "内蒙古"},
|
||||||
|
{"101081201", "阿拉善盟", "阿拉善盟", "内蒙古"},
|
||||||
|
{"101300101", "南宁", "南宁", "广西"},
|
||||||
|
{"101300201", "崇左", "崇左", "广西"},
|
||||||
|
{"101300301", "柳州", "柳州", "广西"},
|
||||||
|
{"101300401", "来宾", "来宾", "广西"},
|
||||||
|
{"101300501", "桂林", "桂林", "广西"},
|
||||||
|
{"101300601", "梧州", "梧州", "广西"},
|
||||||
|
{"101300701", "贺州", "贺州", "广西"},
|
||||||
|
{"101300801", "贵港", "贵港", "广西"},
|
||||||
|
{"101300901", "玉林", "玉林", "广西"},
|
||||||
|
{"101301001", "百色", "百色", "广西"},
|
||||||
|
{"101301101", "钦州", "钦州", "广西"},
|
||||||
|
{"101301201", "河池", "河池", "广西"},
|
||||||
|
{"101301301", "北海", "北海", "广西"},
|
||||||
|
{"101301401", "防城港", "防城港", "广西"},
|
||||||
|
{"101140101", "拉萨", "拉萨", "西藏"},
|
||||||
|
{"101140201", "日喀则", "日喀则", "西藏"},
|
||||||
|
{"101140301", "山南", "山南", "西藏"},
|
||||||
|
{"101140401", "林芝", "林芝", "西藏"},
|
||||||
|
{"101140501", "昌都", "昌都", "西藏"},
|
||||||
|
{"101140601", "那曲", "那曲", "西藏"},
|
||||||
|
{"101140701", "阿里地区", "阿里地区", "西藏"},
|
||||||
|
{"101170101", "银川", "银川", "宁夏"},
|
||||||
|
{"101170201", "石嘴山", "石嘴山", "宁夏"},
|
||||||
|
{"101170301", "吴忠", "吴忠", "宁夏"},
|
||||||
|
{"101170401", "固原", "固原", "宁夏"},
|
||||||
|
{"101170501", "中卫", "中卫", "宁夏"},
|
||||||
|
{"101130101", "乌鲁木齐", "乌鲁木齐", "新疆"},
|
||||||
|
{"101130201", "克拉玛依", "克拉玛依", "新疆"},
|
||||||
|
{"101130301", "石河子", "石河子", "新疆"},
|
||||||
|
{"101130401", "昌吉回族自治州", "昌吉回族自治州", "新疆"},
|
||||||
|
{"101130501", "吐鲁番", "吐鲁番", "新疆"},
|
||||||
|
{"101130601", "巴音郭楞蒙古自治州", "巴音郭楞蒙古自治州", "新疆"},
|
||||||
|
{"101130701", "阿拉尔", "阿拉尔", "新疆"},
|
||||||
|
{"101130801", "阿克苏地区", "阿克苏地区", "新疆"},
|
||||||
|
{"101130901", "喀什地区", "喀什地区", "新疆"},
|
||||||
|
{"101131001", "伊犁哈萨克自治州", "伊犁哈萨克自治州", "新疆"},
|
||||||
|
{"101131101", "塔城地区", "塔城地区", "新疆"},
|
||||||
|
{"101131201", "哈密", "哈密", "新疆"},
|
||||||
|
{"101131301", "和田地区", "和田地区", "新疆"},
|
||||||
|
{"101131401", "阿勒泰地区", "阿勒泰地区", "新疆"},
|
||||||
|
{"101131501", "克孜勒苏柯尔克孜自治州", "克孜勒苏柯尔克孜自治州", "新疆"},
|
||||||
|
{"101131601", "博尔塔拉蒙古自治州", "博尔塔拉蒙古自治州", "新疆"},
|
||||||
|
{"101131701", "图木舒克", "图木舒克", "新疆"},
|
||||||
|
{"101131801", "五家渠", "五家渠", "新疆"},
|
||||||
|
{"101131901", "铁门关", "铁门关", "新疆"},
|
||||||
|
{"101131920", "昆玉", "昆玉", "新疆"},
|
||||||
|
{"101132101", "北屯", "北屯", "新疆"},
|
||||||
|
{"101132201", "双河", "双河", "新疆"},
|
||||||
|
{"101132301", "可克达拉", "可克达拉", "新疆"},
|
||||||
|
{"101132401", "胡杨河", "胡杨河", "新疆"},
|
||||||
|
{"101132501", "新星", "新星", "新疆"},
|
||||||
|
{"101132601", "白杨", "白杨", "新疆"},
|
||||||
|
{"101320101", "香港特别行政区", "香港特别行政区", "香港"},
|
||||||
|
{"101330101", "澳门特别行政区", "澳门特别行政区", "澳门"},
|
||||||
|
{"101231001", "宜兰县", "宜兰县", "台湾"},
|
||||||
|
{"101340101", "台北", "台北", "台湾"},
|
||||||
|
{"101340102", "桃园", "桃园", "台湾"},
|
||||||
|
{"101340103", "新竹", "新竹", "台湾"},
|
||||||
|
{"101340201", "高雄", "高雄", "台湾"},
|
||||||
|
{"101340202", "嘉义", "嘉义", "台湾"},
|
||||||
|
{"101340203", "台南", "台南", "台湾"},
|
||||||
|
{"101340204", "台东县", "台东县", "台湾"},
|
||||||
|
{"101340205", "屏东县", "屏东县", "台湾"},
|
||||||
|
{"101340401", "台中", "台中", "台湾"},
|
||||||
|
{"101340402", "苗栗县", "苗栗县", "台湾"},
|
||||||
|
{"101340403", "彰化县", "彰化县", "台湾"},
|
||||||
|
{"101340404", "南投县", "南投县", "台湾"},
|
||||||
|
{"101340405", "花莲县", "花莲县", "台湾"},
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultCity = City{"101200101", "武汉", "武汉", "湖北"}
|
var defaultCity = City{"101200101", "武汉", "武汉", "湖北"}
|
||||||
|
|||||||
@@ -84,18 +84,32 @@
|
|||||||
<img v-if="bing.url" :src="bingThumb" class="w-16 h-10 object-cover rounded flex-shrink-0">
|
<img v-if="bing.url" :src="bingThumb" class="w-16 h-10 object-cover rounded flex-shrink-0">
|
||||||
<div class="min-w-0">
|
<div class="min-w-0">
|
||||||
<div class="text-xs font-medium text-[var(--text-muted)] truncate">{{ bing.copyright || 'Bing 每日壁纸' }}</div>
|
<div class="text-xs font-medium text-[var(--text-muted)] truncate">{{ bing.copyright || 'Bing 每日壁纸' }}</div>
|
||||||
<div class="text-[10px] text-[var(--text-weak)]">{{ bing.idx > 0 ? `${bing.idx} / ${bing.total}` : '' }}</div>
|
<div class="text-[10px] text-[var(--text-weak)]">{{ bing.total > 0 ? `${bing.idx + 1} / ${bing.total}` : '' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-1 flex-shrink-0">
|
<div class="flex gap-1 flex-shrink-0">
|
||||||
<button class="btn-sm" @click="onBingPrev">◀</button>
|
<button class="btn-sm" @click="onBingPrev">◀</button>
|
||||||
<button class="btn-sm" @click="onBingNext">▶</button>
|
<button class="btn-sm" @click="onBingNext">▶</button>
|
||||||
<button class="btn-sm" @click="onBingFav">{{ bing.fav ? '★' : '☆' }}</button>
|
<button class="btn-sm" @click="onBingFav">{{ bing.fav ? '★' : '☆' }}</button>
|
||||||
|
<button class="btn-sm" @click="onBingDislike">👎</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center px-3.5 py-2 border-t border-[var(--card-divider)]">
|
<div class="flex justify-between items-center px-3.5 py-2 border-t border-[var(--card-divider)]">
|
||||||
<div><div class="text-xs font-medium text-[var(--text-muted)]">定时切换</div><div class="text-[10px] text-[var(--text-weak)] mt-px">每小时自动切换壁纸</div></div>
|
<div><div class="text-xs font-medium text-[var(--text-muted)]">定时切换</div><div class="text-[10px] text-[var(--text-weak)] mt-px">每小时自动切换壁纸</div></div>
|
||||||
<ToggleSwitch v-model="s.bingAutoRefresh" />
|
<ToggleSwitch v-model="s.bingAutoRefresh" />
|
||||||
|
</div>
|
||||||
|
<!-- 市场选择 -->
|
||||||
|
<div class="px-3.5 py-2 border-t border-[var(--card-divider)]">
|
||||||
|
<div class="flex justify-between items-center cursor-pointer select-none" @click="showMarkets = !showMarkets">
|
||||||
|
<div class="text-xs font-medium text-[var(--text-muted)]">壁纸来源地区</div>
|
||||||
|
<span class="text-[10px] text-[var(--text-weak)]">{{ showMarkets ? '收起' : '展开' }} {{ selectedMarkets.length }}/{{ allMarkets.length }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="showMarkets" class="flex flex-wrap gap-2 mt-2">
|
||||||
|
<label v-for="m in allMarkets" :key="m.code" class="flex items-center gap-1.5 text-xs text-[var(--text)] cursor-pointer">
|
||||||
|
<input type="checkbox" :checked="selectedMarkets.includes(m.code)" @change="toggleMarket(m.code)" class="accent-[var(--accent)]">
|
||||||
|
{{ m.label }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 收藏列表 -->
|
<!-- 收藏列表 -->
|
||||||
<div v-if="bingFavs.length" class="px-3.5 py-2 border-t border-[var(--card-divider)]">
|
<div v-if="bingFavs.length" class="px-3.5 py-2 border-t border-[var(--card-divider)]">
|
||||||
@@ -185,9 +199,12 @@ const audioColors = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
// Bing
|
// Bing
|
||||||
const bing = ref<BingState>({ date: '', title: '', copyright: '', url: '', fav: false, idx: 0, total: 0 })
|
const bing = ref<BingState>({ date: '', title: '', copyright: '', url: '', fav: false, idx: 0, total: 0, hidden: false, market: '' })
|
||||||
const bingThumb = ref('')
|
const bingThumb = ref('')
|
||||||
const bingFavs = ref<{ idx: number; thumb: string }[]>([])
|
const bingFavs = ref<{ idx: number; thumb: string }[]>([])
|
||||||
|
const showMarkets = ref(false)
|
||||||
|
const allMarkets = ref<{ code: string; label: string }[]>([])
|
||||||
|
const selectedMarkets = ref<string[]>([])
|
||||||
|
|
||||||
async function loadBingInfo() {
|
async function loadBingInfo() {
|
||||||
const raw = await go.getBingInfo()
|
const raw = await go.getBingInfo()
|
||||||
@@ -199,23 +216,53 @@ async function loadBingInfo() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onBingPrev() { bing.value = JSON.parse(await go.bingPrev()) }
|
async function loadBingMarkets() {
|
||||||
|
const raw = await go.getBingMarkets()
|
||||||
|
const codes = JSON.parse(raw) as string[]
|
||||||
|
selectedMarkets.value = codes
|
||||||
|
allMarkets.value = [
|
||||||
|
{ code: 'zh-CN', label: '中国' },
|
||||||
|
{ code: 'en-US', label: '美国' },
|
||||||
|
{ code: 'ja-JP', label: '日本' },
|
||||||
|
{ code: 'de-DE', label: '德国' },
|
||||||
|
{ code: 'fr-FR', label: '法国' },
|
||||||
|
{ code: 'en-GB', label: '英国' },
|
||||||
|
{ code: 'ko-KR', label: '韩国' },
|
||||||
|
{ code: 'ru-RU', label: '俄罗斯' },
|
||||||
|
{ code: 'pt-BR', label: '巴西' },
|
||||||
|
{ code: 'es-ES', label: '西班牙' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMarket(code: string) {
|
||||||
|
const idx = selectedMarkets.value.indexOf(code)
|
||||||
|
if (idx >= 0) {
|
||||||
|
selectedMarkets.value.splice(idx, 1)
|
||||||
|
} else {
|
||||||
|
selectedMarkets.value.push(code)
|
||||||
|
}
|
||||||
|
go.saveBingMarkets(JSON.stringify(selectedMarkets.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左 ◄ = 上一张(更旧), 右 ► = 下一张(更新)
|
||||||
async function onBingNext() { bing.value = JSON.parse(await go.bingNext()) }
|
async function onBingNext() { bing.value = JSON.parse(await go.bingNext()) }
|
||||||
|
async function onBingPrev() { bing.value = JSON.parse(await go.bingPrev()) }
|
||||||
async function onBingFav() { bing.value = JSON.parse(await go.bingToggleFavorite()); await loadBingFavs() }
|
async function onBingFav() { bing.value = JSON.parse(await go.bingToggleFavorite()); await loadBingFavs() }
|
||||||
async function onBingSetByIdx(idx: number) { bing.value = JSON.parse(await go.bingSetByIdx(idx)) }
|
async function onBingSetByIdx(idx: number) { bing.value = JSON.parse(await go.bingSetByIdx(idx)) }
|
||||||
|
async function onBingDislike() { bing.value = JSON.parse(await go.bingToggleDislike()) }
|
||||||
|
|
||||||
async function loadBingFavs() {
|
async function loadBingFavs() {
|
||||||
const raw = await go.getBingFavorites()
|
const raw = await go.getBingFavorites()
|
||||||
const list = JSON.parse(raw) as string[]
|
const list = JSON.parse(raw) as { date: string; copyright: string; filename: string; idx: number }[]
|
||||||
bingFavs.value = await Promise.all(list.map(async f => ({
|
bingFavs.value = await Promise.all(list.map(async f => ({
|
||||||
idx: parseInt(f.match(/(\d+)/)?.[1] || '0', 10),
|
idx: f.idx,
|
||||||
thumb: await go.bingThumbDataURI(f)
|
thumb: await go.bingThumbDataURI(f.filename)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.s.bingAutoRefresh, v => go.saveBingAutoRefresh(v))
|
watch(() => props.s.bingAutoRefresh, v => go.saveBingAutoRefresh(v))
|
||||||
watch(() => props.s.wallpaperType, t => {
|
watch(() => props.s.wallpaperType, t => {
|
||||||
if (t === 'bing') { loadBingInfo(); loadBingFavs() }
|
if (t === 'bing') { loadBingInfo(); loadBingFavs(); loadBingMarkets() }
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
async function onPickImage() {
|
async function onPickImage() {
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export const go = {
|
|||||||
getBingFavorites: (): Promise<string> => w.getBingFavorites(),
|
getBingFavorites: (): Promise<string> => w.getBingFavorites(),
|
||||||
bingSetByIdx: (idx: number): Promise<string> => w.bingSetByIdx(idx),
|
bingSetByIdx: (idx: number): Promise<string> => w.bingSetByIdx(idx),
|
||||||
bingThumbDataURI: (filename: string): Promise<string> => w.bingThumbDataURI(filename),
|
bingThumbDataURI: (filename: string): Promise<string> => w.bingThumbDataURI(filename),
|
||||||
|
bingToggleDislike: (): Promise<string> => w.bingToggleDislike(),
|
||||||
|
getBingMarkets: (): Promise<string> => w.getBingMarkets(),
|
||||||
|
saveBingMarkets: (markets: string) => w.saveBingMarkets(markets),
|
||||||
pickSolidColor: (): Promise<string> => w.pickSolidColor(),
|
pickSolidColor: (): Promise<string> => w.pickSolidColor(),
|
||||||
pickGradientColor: (): Promise<string> => w.pickGradientColor(),
|
pickGradientColor: (): Promise<string> => w.pickGradientColor(),
|
||||||
addSavedColor: (c1: string, c2: string, gradient: boolean) => w.addSavedColor(c1, c2, gradient),
|
addSavedColor: (c1: string, c2: string, gradient: boolean) => w.addSavedColor(c1, c2, gradient),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export interface WeatherData {
|
export interface WeatherData {
|
||||||
current: string
|
current: string
|
||||||
|
city: string
|
||||||
hourly: HourlyItem[]
|
hourly: HourlyItem[]
|
||||||
daily: DailyItem[]
|
daily: DailyItem[]
|
||||||
}
|
}
|
||||||
@@ -95,4 +96,6 @@ export interface BingState {
|
|||||||
fav: boolean
|
fav: boolean
|
||||||
idx: number
|
idx: number
|
||||||
total: number
|
total: number
|
||||||
|
hidden: boolean
|
||||||
|
market?: string
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
File diff suppressed because one or more lines are too long
+5
-5
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user