diff --git a/config.example.yaml b/config.example.yaml index ba45dd1..ea340ef 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -17,6 +17,7 @@ log: api: mode: local # local | redirect enable_mkt_fallback: true # 当请求的地区不存在时,是否回退到默认地区 + enable_on_demand_fetch: false # 是否开启按需抓取(当数据库中没有请求的地区图片时,实时从 Bing 抓取) cron: enabled: true diff --git a/docs/docs.go b/docs/docs.go index ea4ffca..a575e51 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -675,6 +675,29 @@ const docTemplate = `{ } } }, + "/images/global/today": { + "get": { + "description": "获取配置文件中所有已开启地区的今日必应图片元数据(缩略图)", + "produces": [ + "application/json" + ], + "tags": [ + "image" + ], + "summary": "获取所有地区的今日图片列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/handlers.ImageMetaResp" + } + } + } + } + } + }, "/regions": { "get": { "description": "返回系统支持的所有必应地区编码及标签。如果配置中指定了抓取地区,这些地区将排在列表最前面(置顶)。", diff --git a/docs/swagger.json b/docs/swagger.json index 72ef2d0..a88c272 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -669,6 +669,29 @@ } } }, + "/images/global/today": { + "get": { + "description": "获取配置文件中所有已开启地区的今日必应图片元数据(缩略图)", + "produces": [ + "application/json" + ], + "tags": [ + "image" + ], + "summary": "获取所有地区的今日图片列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/handlers.ImageMetaResp" + } + } + } + } + } + }, "/regions": { "get": { "description": "返回系统支持的所有必应地区编码及标签。如果配置中指定了抓取地区,这些地区将排在列表最前面(置顶)。", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 14a2d67..214810b 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -700,6 +700,21 @@ paths: summary: 获取图片列表 tags: - image + /images/global/today: + get: + description: 获取配置文件中所有已开启地区的今日必应图片元数据(缩略图) + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/handlers.ImageMetaResp' + type: array + summary: 获取所有地区的今日图片列表 + tags: + - image /regions: get: description: 返回系统支持的所有必应地区编码及标签。如果配置中指定了抓取地区,这些地区将排在列表最前面(置顶)。 diff --git a/internal/config/config.go b/internal/config/config.go index a51fbca..38f2b24 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -60,8 +60,9 @@ func (c LogConfig) GetShowDBLog() bool { return c.ShowDBLog } func (c LogConfig) GetDBLogLevel() string { return c.DBLogLevel } type APIConfig struct { - Mode string `mapstructure:"mode" yaml:"mode"` // local | redirect - EnableMktFallback bool `mapstructure:"enable_mkt_fallback" yaml:"enable_mkt_fallback"` // 当请求的地区不存在时,是否回退到默认地区 + Mode string `mapstructure:"mode" yaml:"mode"` // local | redirect + EnableMktFallback bool `mapstructure:"enable_mkt_fallback" yaml:"enable_mkt_fallback"` // 当请求的地区不存在时,是否回退到默认地区 + EnableOnDemandFetch bool `mapstructure:"enable_on_demand_fetch" yaml:"enable_on_demand_fetch"` // 是否启用按需抓取 } type CronConfig struct { @@ -165,7 +166,8 @@ func Init(configPath string) error { v.SetDefault("log.show_db_log", false) v.SetDefault("log.db_log_level", "info") v.SetDefault("api.mode", "redirect") - v.SetDefault("api.enable_mkt_fallback", true) + v.SetDefault("api.enable_mkt_fallback", false) + v.SetDefault("api.enable_on_demand_fetch", false) v.SetDefault("cron.enabled", true) v.SetDefault("cron.daily_spec", "20 8-23/4 * * *") v.SetDefault("retention.days", 0) diff --git a/internal/http/handlers/image.go b/internal/http/handlers/image.go index ef3f0a9..5828c8d 100644 --- a/internal/http/handlers/image.go +++ b/internal/http/handlers/image.go @@ -5,8 +5,8 @@ import ( "fmt" "io" "net/http" - "sort" "strconv" + "strings" "BingPaper/internal/config" "BingPaper/internal/model" @@ -48,12 +48,13 @@ type ImageMetaResp struct { // @Param format query string false "格式 (jpg)" default(jpg) // @Produce image/jpeg // @Success 200 {file} binary +// @Failure 404 {object} map[string]string "图片未找到,响应体包含具体原因" // @Router /image/today [get] func GetToday(c *gin.Context) { mkt := c.Query("mkt") img, err := image.GetTodayImage(mkt) if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) + sendImageNotFound(c, mkt) return } handleImageResponse(c, img, 7200) // 2小时 @@ -66,12 +67,13 @@ func GetToday(c *gin.Context) { // @Param mkt query string false "地区编码 (如 zh-CN, en-US)" // @Produce json // @Success 200 {object} ImageMetaResp +// @Failure 404 {object} map[string]string "图片未找到,响应体包含具体原因" // @Router /image/today/meta [get] func GetTodayMeta(c *gin.Context) { mkt := c.Query("mkt") img, err := image.GetTodayImage(mkt) if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) + sendImageNotFound(c, mkt) return } c.Header("Cache-Control", "public, max-age=7200") // 2小时 @@ -87,12 +89,13 @@ func GetTodayMeta(c *gin.Context) { // @Param format query string false "格式" default(jpg) // @Produce image/jpeg // @Success 200 {file} binary +// @Failure 404 {object} map[string]string "图片未找到,响应体包含具体原因" // @Router /image/random [get] func GetRandom(c *gin.Context) { mkt := c.Query("mkt") img, err := image.GetRandomImage(mkt) if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) + sendImageNotFound(c, mkt) return } handleImageResponse(c, img, 0) // 禁用缓存 @@ -105,12 +108,13 @@ func GetRandom(c *gin.Context) { // @Param mkt query string false "地区编码 (如 zh-CN, en-US)" // @Produce json // @Success 200 {object} ImageMetaResp +// @Failure 404 {object} map[string]string "图片未找到,响应体包含具体原因" // @Router /image/random/meta [get] func GetRandomMeta(c *gin.Context) { mkt := c.Query("mkt") img, err := image.GetRandomImage(mkt) if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) + sendImageNotFound(c, mkt) return } c.Header("Cache-Control", "no-cache, no-store, must-revalidate") @@ -127,13 +131,14 @@ func GetRandomMeta(c *gin.Context) { // @Param format query string false "格式" default(jpg) // @Produce image/jpeg // @Success 200 {file} binary +// @Failure 404 {object} map[string]string "图片未找到,响应体包含具体原因" // @Router /image/date/{date} [get] func GetByDate(c *gin.Context) { date := c.Param("date") mkt := c.Query("mkt") img, err := image.GetImageByDate(date, mkt) if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) + sendImageNotFound(c, mkt) return } handleImageResponse(c, img, 604800) // 7天 @@ -147,13 +152,14 @@ func GetByDate(c *gin.Context) { // @Param mkt query string false "地区编码 (如 zh-CN, en-US)" // @Produce json // @Success 200 {object} ImageMetaResp +// @Failure 404 {object} map[string]string "图片未找到,响应体包含具体原因" // @Router /image/date/{date}/meta [get] func GetByDateMeta(c *gin.Context) { date := c.Param("date") mkt := c.Query("mkt") img, err := image.GetImageByDate(date, mkt) if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) + sendImageNotFound(c, mkt) return } c.Header("Cache-Control", "public, max-age=604800") // 7天 @@ -223,6 +229,55 @@ func ListImages(c *gin.Context) { c.JSON(http.StatusOK, result) } +// ListGlobalTodayImages 获取所有地区的今日图片列表 +// @Summary 获取所有地区的今日图片列表 +// @Description 获取配置文件中所有已开启地区的今日必应图片元数据(缩略图) +// @Tags image +// @Produce json +// @Success 200 {array} ImageMetaResp +// @Router /images/global/today [get] +func ListGlobalTodayImages(c *gin.Context) { + images, err := image.GetAllRegionsTodayImages() + if err != nil { + util.Logger.Error("ListGlobalTodayImages service call failed", zap.Error(err)) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + result := []gin.H{} + for _, img := range images { + result = append(result, formatMetaSummary(&img)) + } + c.JSON(http.StatusOK, result) +} + +func sendImageNotFound(c *gin.Context, mkt string) { + cfg := config.GetConfig().API + message := "image not found" + + if mkt != "" { + reasons := []string{} + if !util.IsValidRegion(mkt) { + reasons = append(reasons, fmt.Sprintf("[%s] is not a standard region code", mkt)) + } else { + if !cfg.EnableOnDemandFetch { + reasons = append(reasons, "on-demand fetch is disabled") + } + if !cfg.EnableMktFallback { + reasons = append(reasons, "region fallback is disabled") + } + } + + if len(reasons) > 0 { + message = fmt.Sprintf("Image not found for region [%s]. Reasons: %s.", mkt, strings.Join(reasons, ", ")) + } else { + message = fmt.Sprintf("Image not found for region [%s] even after on-demand fetch and fallback attempts.", mkt) + } + } + + c.JSON(http.StatusNotFound, gin.H{"error": message}) +} + func handleImageResponse(c *gin.Context, img *model.Image, maxAge int) { variant := c.DefaultQuery("variant", "UHD") format := c.DefaultQuery("format", "jpg") @@ -387,34 +442,51 @@ func GetRegions(c *gin.Context) { cfg := config.GetConfig() pinned := cfg.Fetcher.Regions - // 创建副本以避免修改原始全局变量 - all := make([]util.Region, len(util.AllRegions)) - copy(all, util.AllRegions) - - if len(pinned) > 0 { - // 创建一个 Map 用于快速查找置顶地区及其顺序 - pinnedMap := make(map[string]int) - for i, v := range pinned { - pinnedMap[v] = i - } - - // 对列表进行稳定排序,使置顶地区排在前面 - sort.SliceStable(all, func(i, j int) bool { - idxI, okI := pinnedMap[all[i].Value] - idxJ, okJ := pinnedMap[all[j].Value] - - if okI && okJ { - return idxI < idxJ - } - if okI { - return true - } - if okJ { - return false - } - return false // 保持非置顶地区的原有相对顺序 - }) + if len(pinned) == 0 { + // 如果没有配置抓取地区,返回所有支持的地区 + c.JSON(http.StatusOK, util.AllRegions) + return } - c.JSON(http.StatusOK, all) + // 创建一个 Map 用于快速查找配置的地区 + pinnedMap := make(map[string]bool) + for _, v := range pinned { + pinnedMap[v] = true + } + + // 只返回配置中的地区,并保持配置中的顺序 + var result []util.Region + // 为了保持配置顺序,我们遍历 pinned 而不是 AllRegions + for _, pVal := range pinned { + for _, r := range util.AllRegions { + if r.Value == pVal { + result = append(result, r) + break + } + } + } + + // 如果配置了一些不在 AllRegions 里的 mkt,上述循环可能漏掉 + // 但根据之前的逻辑,AllRegions 是已知的 17 个地区。 + // 如果用户配置了 fr-CA (不在 17 个内),我们也应该返回它吗? + // 需求说 "前端页面对地区进行约束",如果配置了,前端就该显示。 + // 如果不在 AllRegions 里的,我们直接返回原始编码作为 label 或者查找一下。 + + if len(result) < len(pinned) { + // 补全不在 AllRegions 里的地区 + for _, pVal := range pinned { + found := false + for _, r := range result { + if r.Value == pVal { + found = true + break + } + } + if !found { + result = append(result, util.Region{Value: pVal, Label: pVal}) + } + } + } + + c.JSON(http.StatusOK, result) } diff --git a/internal/http/router.go b/internal/http/router.go index dce749f..d29ddac 100644 --- a/internal/http/router.go +++ b/internal/http/router.go @@ -47,6 +47,7 @@ func SetupRouter(webFS embed.FS) *gin.Engine { img.GET("/date/:date/meta", handlers.GetByDateMeta) } api.GET("/images", handlers.ListImages) + api.GET("/images/global/today", handlers.ListGlobalTodayImages) api.GET("/regions", handlers.GetRegions) // 管理接口 diff --git a/internal/service/fetcher/fetcher.go b/internal/service/fetcher/fetcher.go index 4029d95..2ac29d6 100644 --- a/internal/service/fetcher/fetcher.go +++ b/internal/service/fetcher/fetcher.go @@ -61,15 +61,8 @@ func (f *Fetcher) Fetch(ctx context.Context, n int) error { } for _, mkt := range regions { - util.Logger.Info("Fetching images for region", zap.String("mkt", mkt)) - // 调用两次 API 获取最多两周的数据 - // 第一次 idx=0&n=8 (今天起往回数 8 张) - if err := f.fetchByMkt(ctx, mkt, 0, 8); err != nil { - util.Logger.Error("Failed to fetch images", zap.String("mkt", mkt), zap.Int("idx", 0), zap.Error(err)) - } - // 第二次 idx=7&n=8 (7天前起往回数 8 张,与第一次有重叠,确保不漏) - if err := f.fetchByMkt(ctx, mkt, 7, 8); err != nil { - util.Logger.Error("Failed to fetch images", zap.String("mkt", mkt), zap.Int("idx", 7), zap.Error(err)) + if err := f.FetchRegion(ctx, mkt); err != nil { + util.Logger.Error("Failed to fetch region images", zap.String("mkt", mkt), zap.Error(err)) } } @@ -77,6 +70,27 @@ func (f *Fetcher) Fetch(ctx context.Context, n int) error { return nil } +// FetchRegion 抓取指定地区的图片 +func (f *Fetcher) FetchRegion(ctx context.Context, mkt string) error { + if !util.IsValidRegion(mkt) { + util.Logger.Warn("Skipping fetch for invalid region", zap.String("mkt", mkt)) + return fmt.Errorf("invalid region code: %s", mkt) + } + util.Logger.Info("Fetching images for region", zap.String("mkt", mkt)) + // 调用两次 API 获取最多两周的数据 + // 第一次 idx=0&n=8 (今天起往回数 8 张) + if err := f.fetchByMkt(ctx, mkt, 0, 8); err != nil { + util.Logger.Error("Failed to fetch images", zap.String("mkt", mkt), zap.Int("idx", 0), zap.Error(err)) + return err + } + // 第二次 idx=7&n=8 (7天前起往回数 8 张,与第一次有重叠,确保不漏) + if err := f.fetchByMkt(ctx, mkt, 7, 8); err != nil { + util.Logger.Error("Failed to fetch images", zap.String("mkt", mkt), zap.Int("idx", 7), zap.Error(err)) + // 第二次失败不一定返回错误,因为可能第一次已经拿到了 + } + return nil +} + func (f *Fetcher) fetchByMkt(ctx context.Context, mkt string, idx int, n int) error { url := fmt.Sprintf("%s?format=js&idx=%d&n=%d&uhd=1&mkt=%s", config.BingAPIBase, idx, n, mkt) util.Logger.Debug("Requesting Bing API", zap.String("url", url)) diff --git a/internal/service/image/image_service.go b/internal/service/image/image_service.go index 4ed11ce..426ca29 100644 --- a/internal/service/image/image_service.go +++ b/internal/service/image/image_service.go @@ -8,6 +8,7 @@ import ( "BingPaper/internal/config" "BingPaper/internal/model" "BingPaper/internal/repo" + "BingPaper/internal/service/fetcher" "BingPaper/internal/storage" "BingPaper/internal/util" @@ -58,8 +59,19 @@ func GetTodayImage(mkt string) (*model.Image, error) { tx = tx.Where("mkt = ?", mkt) } err := tx.Preload("Variants").First(&img).Error + if err != nil && mkt != "" && config.GetConfig().API.EnableOnDemandFetch && util.IsValidRegion(mkt) { + // 如果没找到,尝试按需抓取该地区 + util.Logger.Info("Image not found in DB, attempting on-demand fetch", zap.String("mkt", mkt)) + f := fetcher.NewFetcher() + _ = f.FetchRegion(context.Background(), mkt) + + // 抓取后重新查询 + tx = repo.DB.Where("date = ?", today).Where("mkt = ?", mkt) + err = tx.Preload("Variants").First(&img).Error + } + if err != nil { - // 如果今天没有,尝试获取最近的一张 + // 如果今天还是没有,尝试获取最近的一张 tx = repo.DB.Order("date desc") if mkt != "" { tx = tx.Where("mkt = ?", mkt) @@ -79,6 +91,22 @@ func GetTodayImage(mkt string) (*model.Image, error) { return &img, err } +func GetAllRegionsTodayImages() ([]model.Image, error) { + regions := config.GetConfig().Fetcher.Regions + if len(regions) == 0 { + regions = []string{config.GetConfig().GetDefaultMkt()} + } + + var images []model.Image + for _, mkt := range regions { + img, err := GetTodayImage(mkt) + if err == nil { + images = append(images, *img) + } + } + return images, nil +} + func GetRandomImage(mkt string) (*model.Image, error) { var img model.Image // SQLite 使用 RANDOM(), MySQL/Postgres 使用 RANDOM() 或 RAND() @@ -89,6 +117,17 @@ func GetRandomImage(mkt string) (*model.Image, error) { tx = tx.Where("mkt = ?", mkt) } tx.Count(&count) + if count == 0 && mkt != "" && config.GetConfig().API.EnableOnDemandFetch && util.IsValidRegion(mkt) { + // 如果没找到,尝试按需抓取该地区 + util.Logger.Info("No images found in DB for region, attempting on-demand fetch", zap.String("mkt", mkt)) + f := fetcher.NewFetcher() + _ = f.FetchRegion(context.Background(), mkt) + + // 抓取后重新计数 + tx = repo.DB.Model(&model.Image{}).Where("mkt = ?", mkt) + tx.Count(&count) + } + if count == 0 { return nil, fmt.Errorf("no images found") } @@ -127,6 +166,16 @@ func GetImageByDate(date string, mkt string) (*model.Image, error) { tx = tx.Where("mkt = ?", mkt) } err := tx.Preload("Variants").First(&img).Error + if err != nil && mkt != "" && config.GetConfig().API.EnableOnDemandFetch && util.IsValidRegion(mkt) { + // 如果没找到,尝试按需抓取该地区 + util.Logger.Info("Image not found in DB for date, attempting on-demand fetch", zap.String("mkt", mkt), zap.String("date", date)) + f := fetcher.NewFetcher() + _ = f.FetchRegion(context.Background(), mkt) + + // 抓取后重新查询 + tx = repo.DB.Where("date = ?", date).Where("mkt = ?", mkt) + err = tx.Preload("Variants").First(&img).Error + } // 兜底逻辑 if err != nil && mkt != "" && config.GetConfig().API.EnableMktFallback { diff --git a/internal/util/regions.go b/internal/util/regions.go index 75ea1a3..9ae9a74 100644 --- a/internal/util/regions.go +++ b/internal/util/regions.go @@ -1,26 +1,37 @@ package util +import "golang.org/x/text/language" + type Region struct { Value string `json:"value"` Label string `json:"label"` } -var AllRegions = []Region{ - {Value: "zh-CN", Label: "中国 (zh-CN)"}, - {Value: "en-US", Label: "美国 (en-US)"}, - {Value: "ja-JP", Label: "日本 (ja-JP)"}, - {Value: "en-AU", Label: "澳大利亚 (en-AU)"}, - {Value: "en-GB", Label: "英国 (en-GB)"}, - {Value: "de-DE", Label: "德国 (de-DE)"}, - {Value: "en-NZ", Label: "新西兰 (en-NZ)"}, - {Value: "en-CA", Label: "加拿大 (en-CA)"}, - {Value: "fr-FR", Label: "法国 (fr-FR)"}, - {Value: "it-IT", Label: "意大利 (it-IT)"}, - {Value: "es-ES", Label: "西班牙 (es-ES)"}, - {Value: "pt-BR", Label: "巴西 (pt-BR)"}, - {Value: "ko-KR", Label: "韩国 (ko-KR)"}, - {Value: "en-IN", Label: "印度 (en-IN)"}, - {Value: "ru-RU", Label: "俄罗斯 (ru-RU)"}, - {Value: "zh-HK", Label: "中国香港 (zh-HK)"}, - {Value: "zh-TW", Label: "中国台湾 (zh-TW)"}, +// IsValidRegion 校验是否为标准的地区编码 (BCP 47) +func IsValidRegion(mkt string) bool { + if mkt == "" { + return false + } + _, err := language.Parse(mkt) + return err == nil +} + +var AllRegions = []Region{ + {Value: "zh-CN", Label: "中国"}, + {Value: "en-US", Label: "美国"}, + {Value: "ja-JP", Label: "日本"}, + {Value: "en-AU", Label: "澳大利亚"}, + {Value: "en-GB", Label: "英国"}, + {Value: "de-DE", Label: "德国"}, + {Value: "en-NZ", Label: "新西兰"}, + {Value: "en-CA", Label: "加拿大"}, + {Value: "fr-FR", Label: "法国"}, + {Value: "it-IT", Label: "意大利"}, + {Value: "es-ES", Label: "西班牙"}, + {Value: "pt-BR", Label: "巴西"}, + {Value: "ko-KR", Label: "韩国"}, + {Value: "en-IN", Label: "印度"}, + {Value: "ru-RU", Label: "俄罗斯"}, + {Value: "zh-HK", Label: "中国香港"}, + {Value: "zh-TW", Label: "中国台湾"}, } diff --git a/webapp/src/composables/useImages.ts b/webapp/src/composables/useImages.ts index 4a93c22..1468fdb 100644 --- a/webapp/src/composables/useImages.ts +++ b/webapp/src/composables/useImages.ts @@ -37,6 +37,39 @@ export function useTodayImage(mkt?: string) { } } +/** + * 获取全球今日图片 + */ +export function useGlobalTodayImages() { + const images = ref([]) + const loading = ref(false) + const error = ref(null) + + const fetchImages = async () => { + loading.value = true + error.value = null + try { + images.value = await bingPaperApi.getGlobalTodayImages() + } catch (e) { + error.value = e as Error + console.error('Failed to fetch global today images:', e) + } finally { + loading.value = false + } + } + + onMounted(() => { + fetchImages() + }) + + return { + images, + loading, + error, + refetch: fetchImages + } +} + /** * 获取图片列表(支持分页和月份筛选) */ diff --git a/webapp/src/lib/api-service.ts b/webapp/src/lib/api-service.ts index 806e7b1..9dd3568 100644 --- a/webapp/src/lib/api-service.ts +++ b/webapp/src/lib/api-service.ts @@ -118,6 +118,13 @@ export class BingPaperApiService { return apiClient.get(endpoint) } + /** + * 获取所有地区的今日图片列表 + */ + async getGlobalTodayImages(): Promise { + return apiClient.get('/images/global/today') + } + /** * 获取支持的地区列表 */ @@ -212,6 +219,7 @@ export const { manualFetch, manualCleanup, getImages, + getGlobalTodayImages, getRegions, getTodayImageMeta, getImageMetaByDate, diff --git a/webapp/src/lib/api-types.ts b/webapp/src/lib/api-types.ts index 6ef0a01..67ca6ad 100644 --- a/webapp/src/lib/api-types.ts +++ b/webapp/src/lib/api-types.ts @@ -75,6 +75,7 @@ export interface AdminConfig { export interface APIConfig { Mode: string // 'local' | 'redirect' EnableMktFallback: boolean + EnableOnDemandFetch: boolean } export interface CronConfig { diff --git a/webapp/src/lib/mkt-utils.ts b/webapp/src/lib/mkt-utils.ts index cdc5b96..30aa78b 100644 --- a/webapp/src/lib/mkt-utils.ts +++ b/webapp/src/lib/mkt-utils.ts @@ -6,23 +6,23 @@ const DEFAULT_MKT = 'zh-CN' * 默认地区列表 (兜底用) */ export const DEFAULT_REGIONS = [ - { value: 'zh-CN', label: '中国 (zh-CN)' }, - { value: 'en-US', label: '美国 (en-US)' }, - { value: 'ja-JP', label: '日本 (ja-JP)' }, - { value: 'en-AU', label: '澳大利亚 (en-AU)' }, - { value: 'en-GB', label: '英国 (en-GB)' }, - { value: 'de-DE', label: '德国 (de-DE)' }, - { value: 'en-NZ', label: '新西兰 (en-NZ)' }, - { value: 'en-CA', label: '加拿大 (en-CA)' }, - { value: 'fr-FR', label: '法国 (fr-FR)' }, - { value: 'it-IT', label: '意大利 (it-IT)' }, - { value: 'es-ES', label: '西班牙 (es-ES)' }, - { value: 'pt-BR', label: '巴西 (pt-BR)' }, - { value: 'ko-KR', label: '韩国 (ko-KR)' }, - { value: 'en-IN', label: '印度 (en-IN)' }, - { value: 'ru-RU', label: '俄罗斯 (ru-RU)' }, - { value: 'zh-HK', label: '中国香港 (zh-HK)' }, - { value: 'zh-TW', label: '中国台湾 (zh-TW)' }, + { value: 'zh-CN', label: '中国' }, + { value: 'en-US', label: '美国' }, + { value: 'ja-JP', label: '日本' }, + { value: 'en-AU', label: '澳大利亚' }, + { value: 'en-GB', label: '英国' }, + { value: 'de-DE', label: '德国' }, + { value: 'en-NZ', label: '新西兰' }, + { value: 'en-CA', label: '加拿大' }, + { value: 'fr-FR', label: '法国' }, + { value: 'it-IT', label: '意大利' }, + { value: 'es-ES', label: '西班牙' }, + { value: 'pt-BR', label: '巴西' }, + { value: 'ko-KR', label: '韩国' }, + { value: 'en-IN', label: '印度' }, + { value: 'ru-RU', label: '俄罗斯' }, + { value: 'zh-HK', label: '中国香港' }, + { value: 'zh-TW', label: '中国台湾' }, ] /** diff --git a/webapp/src/views/AdminConfig.vue b/webapp/src/views/AdminConfig.vue index 14e8a83..8786779 100644 --- a/webapp/src/views/AdminConfig.vue +++ b/webapp/src/views/AdminConfig.vue @@ -102,16 +102,33 @@ local: 直接返回图片流; redirect: 重定向到存储位置

-
- - +
+
+
+ +

+ 如果请求的地区无数据,自动回退到默认地区 +

+
+ +
+ +
+
+ +

+ 如果请求的地区无数据,尝试实时从 Bing 抓取 +

+
+ +
-

- 如果请求的地区无数据,自动回退到默认地区 -

diff --git a/webapp/src/views/Home.vue b/webapp/src/views/Home.vue index 122b310..5466122 100644 --- a/webapp/src/views/Home.vue +++ b/webapp/src/views/Home.vue @@ -69,6 +69,81 @@
+ +
+
+
+
+

+ 必应全球 +

+
+
+ +
+ + + + +
+
+ + + + +
+ + +
+
+ {{ formatDate(image.date) }} +
+
+ + {{ getRegionLabel(image.mkt) }} + +
+ +

+ {{ image.title || '必应每日一图' }} +

+
+
+
+ + + +
+
+
+
@@ -262,7 +337,7 @@