From 626d3895aaf303d1895c1cee953b4717b755aedc Mon Sep 17 00:00:00 2001 From: MaysWind Date: Tue, 27 May 2025 01:01:55 +0800 Subject: [PATCH] allow users to set coordinate display type (#141) --- cmd/user_data.go | 1 + pkg/api/users.go | 9 + pkg/core/coordinate.go | 42 ++++ pkg/models/user.go | 186 +++++++++--------- pkg/services/users.go | 4 + src/components/common/MapView.vue | 12 +- src/components/mobile/MapSheet.vue | 14 +- src/core/coordinate.ts | 76 +++++++ src/core/map.ts | 4 - src/lib/coordinate.ts | 56 ++++++ src/lib/map/amap.ts | 6 +- src/lib/map/baidumap.ts | 6 +- src/lib/map/base.ts | 10 +- src/lib/map/googlemap.ts | 6 +- src/lib/map/leaflet.ts | 9 +- src/locales/de.json | 7 + src/locales/en.json | 7 + src/locales/es.json | 7 + src/locales/helpers.ts | 25 +++ src/locales/it.json | 7 + src/locales/ja.json | 7 + src/locales/ru.json | 7 + src/locales/uk.json | 7 + src/locales/vi.json | 7 + src/locales/zh_Hans.json | 7 + src/locales/zh_Hant.json | 7 + src/models/transaction.ts | 34 +++- src/models/user.ts | 8 + src/stores/user.ts | 6 + .../transactions/TransactionEditPageBase.ts | 4 +- src/views/base/users/UserProfilePageBase.ts | 4 + .../transactions/import/ImportDialog.vue | 4 +- .../transactions/list/dialogs/EditDialog.vue | 10 +- .../settings/tabs/UserBasicSettingTab.vue | 20 ++ src/views/mobile/transactions/EditPage.vue | 6 +- src/views/mobile/users/UserProfilePage.vue | 28 +++ 36 files changed, 516 insertions(+), 144 deletions(-) create mode 100644 pkg/core/coordinate.go create mode 100644 src/core/coordinate.ts delete mode 100644 src/core/map.ts create mode 100644 src/lib/coordinate.ts diff --git a/cmd/user_data.go b/cmd/user_data.go index 5b8897f6..e4ddd298 100644 --- a/cmd/user_data.go +++ b/cmd/user_data.go @@ -903,6 +903,7 @@ func printUserInfo(user *models.User) { fmt.Printf("[DigitGroupingSymbol] %s (%d)\n", user.DigitGroupingSymbol, user.DigitGroupingSymbol) fmt.Printf("[DigitGrouping] %s (%d)\n", user.DigitGrouping, user.DigitGrouping) fmt.Printf("[CurrencyDisplayType] %s (%d)\n", user.CurrencyDisplayType, user.CurrencyDisplayType) + fmt.Printf("[CoordinateDisplayType] %s (%d)\n", user.CoordinateDisplayType, user.CoordinateDisplayType) fmt.Printf("[ExpenseAmountColor] %s (%d)\n", user.ExpenseAmountColor, user.ExpenseAmountColor) fmt.Printf("[IncomeAmountColor] %s (%d)\n", user.IncomeAmountColor, user.IncomeAmountColor) fmt.Printf("[FeatureRestriction] %s (%d)\n", user.FeatureRestriction, user.FeatureRestriction) diff --git a/pkg/api/users.go b/pkg/api/users.go index c9da8015..6418f458 100644 --- a/pkg/api/users.go +++ b/pkg/api/users.go @@ -421,6 +421,15 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.WebContext) (any, *errs.Erro userNew.CurrencyDisplayType = core.CURRENCY_DISPLAY_TYPE_INVALID } + if userUpdateReq.CoordinateDisplayType != nil && *userUpdateReq.CoordinateDisplayType != user.CoordinateDisplayType { + user.CoordinateDisplayType = *userUpdateReq.CoordinateDisplayType + userNew.CoordinateDisplayType = *userUpdateReq.CoordinateDisplayType + modifyProfileBasicInfo = true + anythingUpdate = true + } else { + userNew.CoordinateDisplayType = core.COORDINATE_DISPLAY_TYPE_INVALID + } + if userUpdateReq.ExpenseAmountColor != nil && *userUpdateReq.ExpenseAmountColor != user.ExpenseAmountColor { user.ExpenseAmountColor = *userUpdateReq.ExpenseAmountColor userNew.ExpenseAmountColor = *userUpdateReq.ExpenseAmountColor diff --git a/pkg/core/coordinate.go b/pkg/core/coordinate.go new file mode 100644 index 00000000..24acc170 --- /dev/null +++ b/pkg/core/coordinate.go @@ -0,0 +1,42 @@ +package core + +import "fmt" + +// CoordinateDisplayType represents the display type of geographic coordinates +type CoordinateDisplayType byte + +// Coordinate Display Type +const ( + COORDINATE_DISPLAY_TYPE_DEFAULT CoordinateDisplayType = 0 + COORDINATE_DISPLAY_TYPE_LATITUDE_LONGITUDE_DECIMAL_DEGREES CoordinateDisplayType = 1 + COORDINATE_DISPLAY_TYPE_LONGITUDE_LATITUDE_DECIMAL_DEGREES CoordinateDisplayType = 2 + COORDINATE_DISPLAY_TYPE_LATITUDE_LONGITUDE_DECIMAL_MINUTES CoordinateDisplayType = 3 + COORDINATE_DISPLAY_TYPE_LONGITUDE_LATITUDE_DECIMAL_MINUTES CoordinateDisplayType = 4 + COORDINATE_DISPLAY_TYPE_LATITUDE_LONGITUDE_DEGREES_MINUTES_SECONDS CoordinateDisplayType = 5 + COORDINATE_DISPLAY_TYPE_LONGITUDE_LATITUDE_DEGREES_MINUTES_SECONDS CoordinateDisplayType = 6 + COORDINATE_DISPLAY_TYPE_INVALID CoordinateDisplayType = 255 +) + +// String returns a textual representation of the geographic coordinates display type enum +func (d CoordinateDisplayType) String() string { + switch d { + case COORDINATE_DISPLAY_TYPE_DEFAULT: + return "Default" + case COORDINATE_DISPLAY_TYPE_LATITUDE_LONGITUDE_DECIMAL_DEGREES: + return "Latitude Longitude (Decimal Degrees)" + case COORDINATE_DISPLAY_TYPE_LONGITUDE_LATITUDE_DECIMAL_DEGREES: + return "Longitude Latitude (Decimal Degrees)" + case COORDINATE_DISPLAY_TYPE_LATITUDE_LONGITUDE_DECIMAL_MINUTES: + return "Latitude Longitude (Decimal Minutes)" + case COORDINATE_DISPLAY_TYPE_LONGITUDE_LATITUDE_DECIMAL_MINUTES: + return "Longitude Latitude (Decimal Minutes)" + case COORDINATE_DISPLAY_TYPE_LATITUDE_LONGITUDE_DEGREES_MINUTES_SECONDS: + return "Latitude Longitude (Degrees Minutes Seconds)" + case COORDINATE_DISPLAY_TYPE_LONGITUDE_LATITUDE_DEGREES_MINUTES_SECONDS: + return "Longitude Latitude (Degrees Minutes Seconds)" + case COORDINATE_DISPLAY_TYPE_INVALID: + return "Invalid" + default: + return fmt.Sprintf("Invalid(%d)", int(d)) + } +} diff --git a/pkg/models/user.go b/pkg/models/user.go index 8f96d026..4542944a 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -82,61 +82,63 @@ func (s AmountColorType) String() string { // User represents user data stored in database type User struct { - Uid int64 `xorm:"PK"` - Username string `xorm:"VARCHAR(32) UNIQUE NOT NULL"` - Email string `xorm:"VARCHAR(100) UNIQUE NOT NULL"` - Nickname string `xorm:"VARCHAR(64) NOT NULL"` - Password string `xorm:"VARCHAR(64) NOT NULL"` - Salt string `xorm:"VARCHAR(10) NOT NULL"` - CustomAvatarType string `xorm:"VARCHAR(10)"` - DefaultAccountId int64 - TransactionEditScope TransactionEditScope `xorm:"TINYINT NOT NULL"` - Language string `xorm:"VARCHAR(10)"` - DefaultCurrency string `xorm:"VARCHAR(3) NOT NULL"` - FirstDayOfWeek core.WeekDay `xorm:"TINYINT NOT NULL"` - LongDateFormat core.LongDateFormat `xorm:"TINYINT"` - ShortDateFormat core.ShortDateFormat `xorm:"TINYINT"` - LongTimeFormat core.LongTimeFormat `xorm:"TINYINT"` - ShortTimeFormat core.ShortTimeFormat `xorm:"TINYINT"` - DecimalSeparator core.DecimalSeparator `xorm:"TINYINT"` - DigitGroupingSymbol core.DigitGroupingSymbol `xorm:"TINYINT"` - DigitGrouping core.DigitGroupingType `xorm:"TINYINT"` - CurrencyDisplayType core.CurrencyDisplayType `xorm:"TINYINT"` - ExpenseAmountColor AmountColorType `xorm:"TINYINT"` - IncomeAmountColor AmountColorType `xorm:"TINYINT"` - FeatureRestriction core.UserFeatureRestrictions - Disabled bool - Deleted bool `xorm:"NOT NULL"` - EmailVerified bool `xorm:"NOT NULL"` - CreatedUnixTime int64 - UpdatedUnixTime int64 - DeletedUnixTime int64 - LastLoginUnixTime int64 + Uid int64 `xorm:"PK"` + Username string `xorm:"VARCHAR(32) UNIQUE NOT NULL"` + Email string `xorm:"VARCHAR(100) UNIQUE NOT NULL"` + Nickname string `xorm:"VARCHAR(64) NOT NULL"` + Password string `xorm:"VARCHAR(64) NOT NULL"` + Salt string `xorm:"VARCHAR(10) NOT NULL"` + CustomAvatarType string `xorm:"VARCHAR(10)"` + DefaultAccountId int64 + TransactionEditScope TransactionEditScope `xorm:"TINYINT NOT NULL"` + Language string `xorm:"VARCHAR(10)"` + DefaultCurrency string `xorm:"VARCHAR(3) NOT NULL"` + FirstDayOfWeek core.WeekDay `xorm:"TINYINT NOT NULL"` + LongDateFormat core.LongDateFormat `xorm:"TINYINT"` + ShortDateFormat core.ShortDateFormat `xorm:"TINYINT"` + LongTimeFormat core.LongTimeFormat `xorm:"TINYINT"` + ShortTimeFormat core.ShortTimeFormat `xorm:"TINYINT"` + DecimalSeparator core.DecimalSeparator `xorm:"TINYINT"` + DigitGroupingSymbol core.DigitGroupingSymbol `xorm:"TINYINT"` + DigitGrouping core.DigitGroupingType `xorm:"TINYINT"` + CurrencyDisplayType core.CurrencyDisplayType `xorm:"TINYINT"` + CoordinateDisplayType core.CoordinateDisplayType `xorm:"TINYINT"` + ExpenseAmountColor AmountColorType `xorm:"TINYINT"` + IncomeAmountColor AmountColorType `xorm:"TINYINT"` + FeatureRestriction core.UserFeatureRestrictions + Disabled bool + Deleted bool `xorm:"NOT NULL"` + EmailVerified bool `xorm:"NOT NULL"` + CreatedUnixTime int64 + UpdatedUnixTime int64 + DeletedUnixTime int64 + LastLoginUnixTime int64 } // UserBasicInfo represents a view-object of user basic info type UserBasicInfo struct { - Username string `json:"username"` - Email string `json:"email"` - Nickname string `json:"nickname"` - AvatarUrl string `json:"avatar"` - AvatarProvider string `json:"avatarProvider,omitempty"` - DefaultAccountId int64 `json:"defaultAccountId,string"` - TransactionEditScope TransactionEditScope `json:"transactionEditScope"` - Language string `json:"language"` - DefaultCurrency string `json:"defaultCurrency"` - FirstDayOfWeek core.WeekDay `json:"firstDayOfWeek"` - LongDateFormat core.LongDateFormat `json:"longDateFormat"` - ShortDateFormat core.ShortDateFormat `json:"shortDateFormat"` - LongTimeFormat core.LongTimeFormat `json:"longTimeFormat"` - ShortTimeFormat core.ShortTimeFormat `json:"shortTimeFormat"` - DecimalSeparator core.DecimalSeparator `json:"decimalSeparator"` - DigitGroupingSymbol core.DigitGroupingSymbol `json:"digitGroupingSymbol"` - DigitGrouping core.DigitGroupingType `json:"digitGrouping"` - CurrencyDisplayType core.CurrencyDisplayType `json:"currencyDisplayType"` - ExpenseAmountColor AmountColorType `json:"expenseAmountColor"` - IncomeAmountColor AmountColorType `json:"incomeAmountColor"` - EmailVerified bool `json:"emailVerified"` + Username string `json:"username"` + Email string `json:"email"` + Nickname string `json:"nickname"` + AvatarUrl string `json:"avatar"` + AvatarProvider string `json:"avatarProvider,omitempty"` + DefaultAccountId int64 `json:"defaultAccountId,string"` + TransactionEditScope TransactionEditScope `json:"transactionEditScope"` + Language string `json:"language"` + DefaultCurrency string `json:"defaultCurrency"` + FirstDayOfWeek core.WeekDay `json:"firstDayOfWeek"` + LongDateFormat core.LongDateFormat `json:"longDateFormat"` + ShortDateFormat core.ShortDateFormat `json:"shortDateFormat"` + LongTimeFormat core.LongTimeFormat `json:"longTimeFormat"` + ShortTimeFormat core.ShortTimeFormat `json:"shortTimeFormat"` + DecimalSeparator core.DecimalSeparator `json:"decimalSeparator"` + DigitGroupingSymbol core.DigitGroupingSymbol `json:"digitGroupingSymbol"` + DigitGrouping core.DigitGroupingType `json:"digitGrouping"` + CurrencyDisplayType core.CurrencyDisplayType `json:"currencyDisplayType"` + CoordinateDisplayType core.CoordinateDisplayType `json:"coordinateDisplayType"` + ExpenseAmountColor AmountColorType `json:"expenseAmountColor"` + IncomeAmountColor AmountColorType `json:"incomeAmountColor"` + EmailVerified bool `json:"emailVerified"` } // UserLoginRequest represents all parameters of user login request @@ -177,25 +179,26 @@ type UserResendVerifyEmailRequest struct { // UserProfileUpdateRequest represents all parameters of user updating profile request type UserProfileUpdateRequest struct { - Email string `json:"email" binding:"omitempty,notBlank,max=100,validEmail"` - Nickname string `json:"nickname" binding:"omitempty,notBlank,max=64"` - Password string `json:"password" binding:"omitempty,min=6,max=128"` - OldPassword string `json:"oldPassword" binding:"omitempty,min=6,max=128"` - DefaultAccountId int64 `json:"defaultAccountId,string" binding:"omitempty,min=1"` - TransactionEditScope *TransactionEditScope `json:"transactionEditScope" binding:"omitempty,min=0,max=6"` - Language string `json:"language" binding:"omitempty,min=2,max=16"` - DefaultCurrency string `json:"defaultCurrency" binding:"omitempty,len=3,validCurrency"` - FirstDayOfWeek *core.WeekDay `json:"firstDayOfWeek" binding:"omitempty,min=0,max=6"` - LongDateFormat *core.LongDateFormat `json:"longDateFormat" binding:"omitempty,min=0,max=3"` - ShortDateFormat *core.ShortDateFormat `json:"shortDateFormat" binding:"omitempty,min=0,max=3"` - LongTimeFormat *core.LongTimeFormat `json:"longTimeFormat" binding:"omitempty,min=0,max=3"` - ShortTimeFormat *core.ShortTimeFormat `json:"shortTimeFormat" binding:"omitempty,min=0,max=3"` - DecimalSeparator *core.DecimalSeparator `json:"decimalSeparator" binding:"omitempty,min=0,max=3"` - DigitGroupingSymbol *core.DigitGroupingSymbol `json:"digitGroupingSymbol" binding:"omitempty,min=0,max=4"` - DigitGrouping *core.DigitGroupingType `json:"digitGrouping" binding:"omitempty,min=0,max=2"` - CurrencyDisplayType *core.CurrencyDisplayType `json:"currencyDisplayType" binding:"omitempty,min=0,max=11"` - ExpenseAmountColor *AmountColorType `json:"expenseAmountColor" binding:"omitempty,min=0,max=4"` - IncomeAmountColor *AmountColorType `json:"incomeAmountColor" binding:"omitempty,min=0,max=4"` + Email string `json:"email" binding:"omitempty,notBlank,max=100,validEmail"` + Nickname string `json:"nickname" binding:"omitempty,notBlank,max=64"` + Password string `json:"password" binding:"omitempty,min=6,max=128"` + OldPassword string `json:"oldPassword" binding:"omitempty,min=6,max=128"` + DefaultAccountId int64 `json:"defaultAccountId,string" binding:"omitempty,min=1"` + TransactionEditScope *TransactionEditScope `json:"transactionEditScope" binding:"omitempty,min=0,max=6"` + Language string `json:"language" binding:"omitempty,min=2,max=16"` + DefaultCurrency string `json:"defaultCurrency" binding:"omitempty,len=3,validCurrency"` + FirstDayOfWeek *core.WeekDay `json:"firstDayOfWeek" binding:"omitempty,min=0,max=6"` + LongDateFormat *core.LongDateFormat `json:"longDateFormat" binding:"omitempty,min=0,max=3"` + ShortDateFormat *core.ShortDateFormat `json:"shortDateFormat" binding:"omitempty,min=0,max=3"` + LongTimeFormat *core.LongTimeFormat `json:"longTimeFormat" binding:"omitempty,min=0,max=3"` + ShortTimeFormat *core.ShortTimeFormat `json:"shortTimeFormat" binding:"omitempty,min=0,max=3"` + DecimalSeparator *core.DecimalSeparator `json:"decimalSeparator" binding:"omitempty,min=0,max=3"` + DigitGroupingSymbol *core.DigitGroupingSymbol `json:"digitGroupingSymbol" binding:"omitempty,min=0,max=4"` + DigitGrouping *core.DigitGroupingType `json:"digitGrouping" binding:"omitempty,min=0,max=2"` + CurrencyDisplayType *core.CurrencyDisplayType `json:"currencyDisplayType" binding:"omitempty,min=0,max=11"` + CoordinateDisplayType *core.CoordinateDisplayType `json:"coordinateDisplayType" binding:"omitempty,min=0,max=6"` + ExpenseAmountColor *AmountColorType `json:"expenseAmountColor" binding:"omitempty,min=0,max=4"` + IncomeAmountColor *AmountColorType `json:"incomeAmountColor" binding:"omitempty,min=0,max=4"` } // UserProfileUpdateResponse represents the data returns to frontend after updating profile @@ -255,27 +258,28 @@ func (u *User) CanEditTransactionByTransactionTime(transactionTime int64, utcOff // ToUserBasicInfo returns a user basic view-object according to database model func (u *User) ToUserBasicInfo(avatarProvider core.UserAvatarProviderType, avatarUrl string) *UserBasicInfo { return &UserBasicInfo{ - Username: u.Username, - Email: u.Email, - Nickname: u.Nickname, - AvatarUrl: avatarUrl, - AvatarProvider: string(avatarProvider), - DefaultAccountId: u.DefaultAccountId, - TransactionEditScope: u.TransactionEditScope, - Language: u.Language, - DefaultCurrency: u.DefaultCurrency, - FirstDayOfWeek: u.FirstDayOfWeek, - LongDateFormat: u.LongDateFormat, - ShortDateFormat: u.ShortDateFormat, - LongTimeFormat: u.LongTimeFormat, - ShortTimeFormat: u.ShortTimeFormat, - DecimalSeparator: u.DecimalSeparator, - DigitGroupingSymbol: u.DigitGroupingSymbol, - DigitGrouping: u.DigitGrouping, - CurrencyDisplayType: u.CurrencyDisplayType, - ExpenseAmountColor: u.ExpenseAmountColor, - IncomeAmountColor: u.IncomeAmountColor, - EmailVerified: u.EmailVerified, + Username: u.Username, + Email: u.Email, + Nickname: u.Nickname, + AvatarUrl: avatarUrl, + AvatarProvider: string(avatarProvider), + DefaultAccountId: u.DefaultAccountId, + TransactionEditScope: u.TransactionEditScope, + Language: u.Language, + DefaultCurrency: u.DefaultCurrency, + FirstDayOfWeek: u.FirstDayOfWeek, + LongDateFormat: u.LongDateFormat, + ShortDateFormat: u.ShortDateFormat, + LongTimeFormat: u.LongTimeFormat, + ShortTimeFormat: u.ShortTimeFormat, + DecimalSeparator: u.DecimalSeparator, + DigitGroupingSymbol: u.DigitGroupingSymbol, + DigitGrouping: u.DigitGrouping, + CurrencyDisplayType: u.CurrencyDisplayType, + CoordinateDisplayType: u.CoordinateDisplayType, + ExpenseAmountColor: u.ExpenseAmountColor, + IncomeAmountColor: u.IncomeAmountColor, + EmailVerified: u.EmailVerified, } } diff --git a/pkg/services/users.go b/pkg/services/users.go index 55056445..de1ddd97 100644 --- a/pkg/services/users.go +++ b/pkg/services/users.go @@ -321,6 +321,10 @@ func (s *UserService) UpdateUser(c core.Context, user *models.User, modifyUserLa updateCols = append(updateCols, "currency_display_type") } + if core.COORDINATE_DISPLAY_TYPE_DEFAULT <= user.CoordinateDisplayType && user.CoordinateDisplayType <= core.COORDINATE_DISPLAY_TYPE_LONGITUDE_LATITUDE_DEGREES_MINUTES_SECONDS { + updateCols = append(updateCols, "coordinate_display_type") + } + if models.AMOUNT_COLOR_TYPE_DEFAULT <= user.ExpenseAmountColor && user.ExpenseAmountColor <= models.AMOUNT_COLOR_TYPE_BLACK_OR_WHITE { updateCols = append(updateCols, "expense_amount_color") } diff --git a/src/components/common/MapView.vue b/src/components/common/MapView.vue index 94586ddc..be356ec6 100644 --- a/src/components/common/MapView.vue +++ b/src/components/common/MapView.vue @@ -13,7 +13,7 @@ import { ref, computed, useTemplateRef } from 'vue'; import { useI18n } from '@/locales/helpers.ts'; -import type { MapPosition } from '@/core/map.ts'; +import type { Coordinate } from '@/core/coordinate.ts'; import type { MapInstance } from '@/lib/map/base.ts'; import { createMapInstance } from '@/lib/map/index.ts'; @@ -21,18 +21,18 @@ const props = defineProps<{ height?: string; mapClass?: string; mapStyle?: Record; - geoLocation?: MapPosition; + geoLocation?: Coordinate; }>(); const emit = defineEmits<{ - (e: 'click', geoLocation: MapPosition): void; + (e: 'click', geoLocation: Coordinate): void; }>(); const { tt, getCurrentLanguageInfo } = useI18n(); const mapContainer = useTemplateRef('mapContainer'); const mapInstance = ref(createMapInstance()); -const initCenter = ref({ +const initCenter = ref({ latitude: 0, longitude: 0 }); @@ -92,7 +92,7 @@ function initMapView(): void { zoomIn: tt('Zoom in'), zoomOut: tt('Zoom out'), }, - onClick: (geoLocation: MapPosition) => { + onClick: (geoLocation: Coordinate) => { emit('click', geoLocation); } }); @@ -113,7 +113,7 @@ function initMapView(): void { } } -function setMarkerPosition(geoLocation?: MapPosition): void { +function setMarkerPosition(geoLocation?: Coordinate): void { if (!mapInstance.value) { return; } diff --git a/src/components/mobile/MapSheet.vue b/src/components/mobile/MapSheet.vue index 4f270576..86fe59d1 100644 --- a/src/components/mobile/MapSheet.vue +++ b/src/components/mobile/MapSheet.vue @@ -39,20 +39,20 @@ import MapView from '@/components/common/MapView.vue'; import { useI18n } from '@/locales/helpers.ts'; -import type { MapPosition } from '@/core/map.ts'; +import type { Coordinate } from '@/core/coordinate.ts'; import { isSupportGetGeoLocationByClick } from '@/lib/map/index.ts'; type MapViewType = InstanceType; const props = defineProps<{ - modelValue?: MapPosition; + modelValue?: Coordinate; setGeoLocationByClickMap?: boolean; show: boolean; }>(); const emit = defineEmits<{ - (e: 'update:modelValue', value: MapPosition | undefined): void; + (e: 'update:modelValue', value: Coordinate | undefined): void; (e: 'update:setGeoLocationByClickMap', value: boolean): void; (e: 'update:show', value: boolean): void; }>(); @@ -61,7 +61,7 @@ const { tt } = useI18n(); const map = useTemplateRef('map'); -const geoLocation = computed({ +const geoLocation = computed({ get: () => { return props.modelValue; }, @@ -70,10 +70,10 @@ const geoLocation = computed({ } }); -function updateSpecifiedGeoLocation(mapPosition: MapPosition): void { +function updateSpecifiedGeoLocation(coordinate: Coordinate): void { if (isSupportGetGeoLocationByClick() && props.setGeoLocationByClickMap) { - geoLocation.value = mapPosition; - map.value?.setMarkerPosition(mapPosition); + geoLocation.value = coordinate; + map.value?.setMarkerPosition(coordinate); } } diff --git a/src/core/coordinate.ts b/src/core/coordinate.ts new file mode 100644 index 00000000..2402f8f3 --- /dev/null +++ b/src/core/coordinate.ts @@ -0,0 +1,76 @@ +import type { TypeAndName } from '@/core/base.ts'; + +export interface Coordinate { + latitude: number; + longitude: number; +} + +export enum CoordinateDisplayOrder { + LatitudeLongitude = 0, + LongitudeLatitude = 1 +} + +export enum CoordinateDisplayFormat { + DecimalDegrees = 0, + DecimalMinutes = 1, + DegreesMinutesSeconds = 2 +} + +export enum CoordinateDirectionFormat { + Signed = 0, + Directional = 1 +} + +export class CoordinateDisplayType implements TypeAndName { + private static readonly allInstances: CoordinateDisplayType[] = []; + private static readonly allInstancesByType: Record = {}; + + public static readonly SystemDefaultType: number = 0; + public static readonly LatitudeLongitudeDecimalDegrees = new CoordinateDisplayType(1, 'Latitude Longitude D.D°', CoordinateDisplayOrder.LatitudeLongitude, CoordinateDisplayFormat.DecimalDegrees, CoordinateDirectionFormat.Signed); + public static readonly LongitudeLatitudeDecimalDegrees = new CoordinateDisplayType(2, 'Longitude Latitude D.D°', CoordinateDisplayOrder.LongitudeLatitude, CoordinateDisplayFormat.DecimalDegrees, CoordinateDirectionFormat.Signed); + public static readonly LatitudeLongitudeDecimalMinutes = new CoordinateDisplayType(3, 'Latitude Longitude D°M.M\'', CoordinateDisplayOrder.LatitudeLongitude, CoordinateDisplayFormat.DecimalMinutes, CoordinateDirectionFormat.Directional); + public static readonly LongitudeLatitudeDecimalMinutes = new CoordinateDisplayType(4, 'Longitude Latitude D°M.M\'', CoordinateDisplayOrder.LongitudeLatitude, CoordinateDisplayFormat.DecimalMinutes, CoordinateDirectionFormat.Directional); + public static readonly LatitudeLongitudeDegreesMinutesSeconds = new CoordinateDisplayType(5, 'Latitude Longitude D°M\'S"', CoordinateDisplayOrder.LatitudeLongitude, CoordinateDisplayFormat.DegreesMinutesSeconds, CoordinateDirectionFormat.Directional); + public static readonly LongitudeLatitudeDegreesMinutesSeconds = new CoordinateDisplayType(6, 'Longitude Latitude D°M\'S"', CoordinateDisplayOrder.LongitudeLatitude, CoordinateDisplayFormat.DegreesMinutesSeconds, CoordinateDirectionFormat.Directional); + + public static readonly Default = CoordinateDisplayType.LatitudeLongitudeDecimalDegrees; + + public readonly type: number; + public readonly name: string; + public readonly displayOrder: CoordinateDisplayOrder; + public readonly displayFormat: CoordinateDisplayFormat; + public readonly directionFormat: CoordinateDirectionFormat; + + private constructor(type: number, name: string, displayOrder: CoordinateDisplayOrder, displayFormat: CoordinateDisplayFormat, directionFormat: CoordinateDirectionFormat) { + this.type = type; + this.name = name; + this.displayOrder = displayOrder; + this.displayFormat = displayFormat; + this.directionFormat = directionFormat; + + CoordinateDisplayType.allInstances.push(this); + CoordinateDisplayType.allInstancesByType[type] = this; + } + + public static values(): CoordinateDisplayType[] { + return CoordinateDisplayType.allInstances; + } + + public static valueOf(type: number): CoordinateDisplayType | undefined { + return CoordinateDisplayType.allInstancesByType[type]; + } +} + +export function getNormalizedCoordinate(value: Coordinate): Coordinate { + if (!value) { + return value; + } + + const normalizedLatitude = Math.max(-90, Math.min(90, value.latitude)); + const normalizedLongitude = ((value.longitude + 180) % 360 + 360) % 360 - 180; + + return { + latitude: normalizedLatitude, + longitude: normalizedLongitude + }; +} diff --git a/src/core/map.ts b/src/core/map.ts deleted file mode 100644 index 65d73d4e..00000000 --- a/src/core/map.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface MapPosition { - latitude: number; - longitude: number; -} diff --git a/src/lib/coordinate.ts b/src/lib/coordinate.ts new file mode 100644 index 00000000..0b7bd432 --- /dev/null +++ b/src/lib/coordinate.ts @@ -0,0 +1,56 @@ +import { + type Coordinate, + CoordinateDisplayOrder, + CoordinateDisplayFormat, + CoordinateDirectionFormat, + CoordinateDisplayType, + getNormalizedCoordinate +} from '@/core/coordinate.ts'; + +export function formatCoordinate(value: Coordinate, coordinateDisplayType: number): string { + if (!value) { + return ''; + } + + value = getNormalizedCoordinate(value); + + const displayType = CoordinateDisplayType.valueOf(coordinateDisplayType) || CoordinateDisplayType.Default; + const formattedLatitude = formatCoordinateValue(value.latitude, 'N', 'S', displayType.displayFormat, displayType.directionFormat); + const formattedLongitude = formatCoordinateValue(value.longitude, 'E', 'W', displayType.displayFormat, displayType.directionFormat); + + if (displayType.displayOrder === CoordinateDisplayOrder.LatitudeLongitude) { + return `${formattedLatitude}, ${formattedLongitude}`; + } else if (displayType.displayOrder === CoordinateDisplayOrder.LongitudeLatitude) { + return `${formattedLongitude}, ${formattedLatitude}`; + } else { + return ''; + } +} + +function formatCoordinateValue(value: number, positiveDirectionName: string, negativeDirectionName: string, displayFormat: CoordinateDisplayFormat, directionFormat: CoordinateDirectionFormat): string { + let prefix = ''; + let suffix = ''; + + if (directionFormat === CoordinateDirectionFormat.Signed) { + prefix = value >= 0 ? '' : '-'; + } else if (directionFormat === CoordinateDirectionFormat.Directional) { + suffix = value >= 0 ? positiveDirectionName : negativeDirectionName; + } + + value = Math.abs(value); + + if (displayFormat === CoordinateDisplayFormat.DecimalDegrees) { + return `${prefix}${value.toFixed(6)}${suffix}`; + } else if (displayFormat === CoordinateDisplayFormat.DecimalMinutes) { + const degrees = Math.floor(value); + const minutes = (value - degrees) * 60; + return `${prefix}${degrees}°${minutes.toFixed(5)}'${suffix}`; + } else if (displayFormat === CoordinateDisplayFormat.DegreesMinutesSeconds) { + const degrees = Math.floor(value); + const minutes = Math.floor((value - degrees) * 60); + const seconds = (value - degrees - minutes / 60) * 3600; + return `${prefix}${degrees}°${minutes}'${seconds.toFixed(4)}"${suffix}`; + } else { + return ''; + } +} diff --git a/src/lib/map/amap.ts b/src/lib/map/amap.ts index 125dfc40..42ef8ff0 100644 --- a/src/lib/map/amap.ts +++ b/src/lib/map/amap.ts @@ -1,6 +1,6 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck -import type { MapPosition } from '@/core/map.ts'; +import type { Coordinate } from '@/core/coordinate.ts'; import type { MapProvider, MapInstance, MapInstanceInitOptions } from './base.ts'; import { asyncLoadAssets } from '@/lib/misc.ts'; @@ -105,7 +105,7 @@ export class AmapMapInstance implements MapInstance { this.inited = true; } - public setMapCenterTo(center: MapPosition, zoomLevel: number): void { + public setMapCenterTo(center: Coordinate, zoomLevel: number): void { if (!AmapMapProvider.AMap || !this.amapInstance) { return; } @@ -146,7 +146,7 @@ export class AmapMapInstance implements MapInstance { }); } - public setMapCenterMarker(position: MapPosition): void { + public setMapCenterMarker(position: Coordinate): void { if (!AmapMapProvider.AMap || !this.amapInstance) { return; } diff --git a/src/lib/map/baidumap.ts b/src/lib/map/baidumap.ts index 6f0776cc..ab22d8d3 100644 --- a/src/lib/map/baidumap.ts +++ b/src/lib/map/baidumap.ts @@ -1,6 +1,6 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck -import type { MapPosition } from '@/core/map.ts'; +import type { Coordinate } from '@/core/coordinate.ts'; import type { MapProvider, MapInstance, MapInstanceInitOptions } from './base.ts'; import { asyncLoadAssets } from '@/lib/misc.ts'; @@ -96,7 +96,7 @@ export class BaiduMapInstance implements MapInstance { this.inited = true; } - public setMapCenterTo(center: MapPosition, zoomLevel: number): void { + public setMapCenterTo(center: Coordinate, zoomLevel: number): void { if (!BaiduMapProvider.BMap || !this.baiduMapInstance) { return; } @@ -141,7 +141,7 @@ export class BaiduMapInstance implements MapInstance { } } - public setMapCenterMarker(position: MapPosition): void { + public setMapCenterMarker(position: Coordinate): void { if (!BaiduMapProvider.BMap || !this.baiduMapInstance) { return; } diff --git a/src/lib/map/base.ts b/src/lib/map/base.ts index e2a391a0..33c7f173 100644 --- a/src/lib/map/base.ts +++ b/src/lib/map/base.ts @@ -1,4 +1,4 @@ -import type { MapPosition } from '@/core/map.ts'; +import type { Coordinate } from '@/core/coordinate.ts'; export interface MapProvider { getWebsite(): string; @@ -13,18 +13,18 @@ export interface MapInstance { readonly defaultZoomLevel: number; readonly minZoomLevel: number; initMapInstance(mapContainer: HTMLElement, options: MapInstanceInitOptions): void; - setMapCenterTo(center: MapPosition, zoomLevel: number): void; - setMapCenterMarker(position: MapPosition): void; + setMapCenterTo(center: Coordinate, zoomLevel: number): void; + setMapCenterMarker(position: Coordinate): void; removeMapCenterMarker(): void; } export interface MapInstanceInitOptions { readonly language?: string; - readonly initCenter: MapPosition; + readonly initCenter: Coordinate; readonly zoomLevel: number; readonly text: { readonly zoomIn: string; readonly zoomOut: string; }; - readonly onClick?: (position: MapPosition) => void; + readonly onClick?: (position: Coordinate) => void; } diff --git a/src/lib/map/googlemap.ts b/src/lib/map/googlemap.ts index cac2293e..ec7adbbc 100644 --- a/src/lib/map/googlemap.ts +++ b/src/lib/map/googlemap.ts @@ -1,6 +1,6 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck -import type { MapPosition } from '@/core/map.ts'; +import type { Coordinate } from '@/core/coordinate.ts'; import type { MapProvider, MapInstance, MapInstanceInitOptions } from './base.ts'; import { asyncLoadAssets } from '@/lib/misc.ts'; @@ -94,7 +94,7 @@ export class GoogleMapInstance implements MapInstance { this.inited = true; } - public setMapCenterTo(center: MapPosition, zoomLevel: number): void { + public setMapCenterTo(center: Coordinate, zoomLevel: number): void { if (!GoogleMapProvider.GoogleMap || !this.googleMapInstance) { return; } @@ -106,7 +106,7 @@ export class GoogleMapInstance implements MapInstance { this.googleMapInstance.setZoom(zoomLevel); } - public setMapCenterMarker(position: MapPosition): void { + public setMapCenterMarker(position: Coordinate): void { if (!GoogleMapProvider.GoogleMap || !this.googleMapInstance) { return; } diff --git a/src/lib/map/leaflet.ts b/src/lib/map/leaflet.ts index 164afe35..cb3c1169 100644 --- a/src/lib/map/leaflet.ts +++ b/src/lib/map/leaflet.ts @@ -1,6 +1,6 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck -import type { MapPosition } from '@/core/map.ts'; +import type { Coordinate } from '@/core/coordinate.ts'; import { type LeafletTileSource, type LeafletTileSourceExtraParam, LEAFLET_TILE_SOURCES } from '@/consts/map.ts'; @@ -97,8 +97,7 @@ export class LeafletMapInstance implements MapInstance { center: [ options.initCenter.latitude, options.initCenter.longitude ], zoom: options.zoomLevel, attributionControl: false, - zoomControl: false, - worldCopyJump: true + zoomControl: false }); let tileUrlFormat, tileUrlSubDomains, annotationUrlFormat, annotationUrlSubDomains: string | undefined; @@ -180,7 +179,7 @@ export class LeafletMapInstance implements MapInstance { this.inited = true; } - public setMapCenterTo(center: MapPosition, zoomLevel: number): void { + public setMapCenterTo(center: Coordinate, zoomLevel: number): void { if (!this.leafletInstance) { return; } @@ -188,7 +187,7 @@ export class LeafletMapInstance implements MapInstance { this.leafletInstance.setView([ center.latitude, center.longitude ], zoomLevel); } - public setMapCenterMarker(position: MapPosition): void { + public setMapCenterMarker(position: Coordinate): void { if (!LeafletMapProvider.Leaflet || !this.leafletInstance) { return; } diff --git a/src/locales/de.json b/src/locales/de.json index 3b94aa09..3bf7abcb 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1826,6 +1826,13 @@ "Currency Unit": "Währungseinheit", "Currency Name": "Währungsname", "Currency Symbol": "Währungssymbol", + "Geographic Location Format": "Geographic Location Format", + "Latitude Longitude D.D°": "Latitude Longitude D.D°", + "Longitude Latitude D.D°": "Longitude Latitude D.D°", + "Latitude Longitude D°M.M'": "Latitude Longitude D°M.M'", + "Longitude Latitude D°M.M'": "Longitude Latitude D°M.M'", + "Latitude Longitude D°M'S\"": "Latitude Longitude D°M'S\"", + "Longitude Latitude D°M'S\"": "Longitude Latitude D°M'S\"", "Expense Amount Color": "Farbe des Ausgabenbetrags", "Income Amount Color": "Farbe des Einnahmenbetrags", "Show Account Balance": "Kontostand anzeigen", diff --git a/src/locales/en.json b/src/locales/en.json index ad348bf9..316098ec 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1826,6 +1826,13 @@ "Currency Unit": "Currency Unit", "Currency Name": "Currency Name", "Currency Symbol": "Currency Symbol", + "Geographic Location Format": "Geographic Location Format", + "Latitude Longitude D.D°": "Latitude Longitude D.D°", + "Longitude Latitude D.D°": "Longitude Latitude D.D°", + "Latitude Longitude D°M.M'": "Latitude Longitude D°M.M'", + "Longitude Latitude D°M.M'": "Longitude Latitude D°M.M'", + "Latitude Longitude D°M'S\"": "Latitude Longitude D°M'S\"", + "Longitude Latitude D°M'S\"": "Longitude Latitude D°M'S\"", "Expense Amount Color": "Expense Amount Color", "Income Amount Color": "Income Amount Color", "Show Account Balance": "Show Account Balance", diff --git a/src/locales/es.json b/src/locales/es.json index c84d9533..3a73db82 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1826,6 +1826,13 @@ "Currency Unit": "Unidad monetaria", "Currency Name": "Nombre de moneda", "Currency Symbol": "Símbolo de moneda", + "Geographic Location Format": "Geographic Location Format", + "Latitude Longitude D.D°": "Latitude Longitude D.D°", + "Longitude Latitude D.D°": "Longitude Latitude D.D°", + "Latitude Longitude D°M.M'": "Latitude Longitude D°M.M'", + "Longitude Latitude D°M.M'": "Longitude Latitude D°M.M'", + "Latitude Longitude D°M'S\"": "Latitude Longitude D°M'S\"", + "Longitude Latitude D°M'S\"": "Longitude Latitude D°M'S\"", "Expense Amount Color": "Color del importe del gasto", "Income Amount Color": "Color del importe del ingreso", "Show Account Balance": "Mostrar saldo de cuenta", diff --git a/src/locales/helpers.ts b/src/locales/helpers.ts index 7d0ec205..c503f86d 100644 --- a/src/locales/helpers.ts +++ b/src/locales/helpers.ts @@ -46,6 +46,10 @@ import { CurrencySortingType } from '@/core/currency.ts'; +import { + CoordinateDisplayType +} from '@/core/coordinate.ts'; + import { PresetAmountColor } from '@/core/color.ts'; @@ -385,6 +389,26 @@ export function useI18n() { return ret; } + function getLocalizedDisplayNameAndTypeWithSystemDefault(typeAndNames: TypeAndName[], defaultValue: number, defaultType: TypeAndName): TypeAndDisplayName[] { + const ret: TypeAndDisplayName[] = []; + + ret.push({ + type: defaultValue, + displayName: t('System Default') + (defaultType.name ? ` (${t(defaultType.name)})` : '') + }); + + for (let i = 0; i < typeAndNames.length; i++) { + const nameAndType = typeAndNames[i]; + + ret.push({ + type: nameAndType.type, + displayName: t(nameAndType.name) + }); + } + + return ret; + } + function getLocalizedNumeralSeparatorFormats(allSeparatorArray: T[], localeDefaultType: T | undefined, systemDefaultType: T, languageDefaultValue: number): LocalizedNumeralSymbolType[] { let defaultSeparatorType: T | undefined = localeDefaultType; @@ -1699,6 +1723,7 @@ export function useI18n() { getAllDigitGroupingTypes, getAllCurrencyDisplayTypes, getAllCurrencySortingTypes: () => getLocalizedDisplayNameAndType(CurrencySortingType.values()), + getAllCoordinateDisplayTypes: () => getLocalizedDisplayNameAndTypeWithSystemDefault(CoordinateDisplayType.values(), CoordinateDisplayType.SystemDefaultType, CoordinateDisplayType.Default), getAllExpenseAmountColors: () => getAllExpenseIncomeAmountColors(CategoryType.Expense), getAllIncomeAmountColors: () => getAllExpenseIncomeAmountColors(CategoryType.Income), getAllAccountCategories, diff --git a/src/locales/it.json b/src/locales/it.json index 3e001732..2290740f 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -1826,6 +1826,13 @@ "Currency Unit": "Unità valuta", "Currency Name": "Nome valuta", "Currency Symbol": "Simbolo valuta", + "Geographic Location Format": "Geographic Location Format", + "Latitude Longitude D.D°": "Latitude Longitude D.D°", + "Longitude Latitude D.D°": "Longitude Latitude D.D°", + "Latitude Longitude D°M.M'": "Latitude Longitude D°M.M'", + "Longitude Latitude D°M.M'": "Longitude Latitude D°M.M'", + "Latitude Longitude D°M'S\"": "Latitude Longitude D°M'S\"", + "Longitude Latitude D°M'S\"": "Longitude Latitude D°M'S\"", "Expense Amount Color": "Colore importo spesa", "Income Amount Color": "Colore importo entrata", "Show Account Balance": "Mostra saldo conto", diff --git a/src/locales/ja.json b/src/locales/ja.json index edbdc4d0..a458fac3 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1826,6 +1826,13 @@ "Currency Unit": "通貨単位", "Currency Name": "通貨名", "Currency Symbol": "通貨記号", + "Geographic Location Format": "Geographic Location Format", + "Latitude Longitude D.D°": "Latitude Longitude D.D°", + "Longitude Latitude D.D°": "Longitude Latitude D.D°", + "Latitude Longitude D°M.M'": "Latitude Longitude D°M.M'", + "Longitude Latitude D°M.M'": "Longitude Latitude D°M.M'", + "Latitude Longitude D°M'S\"": "Latitude Longitude D°M'S\"", + "Longitude Latitude D°M'S\"": "Longitude Latitude D°M'S\"", "Expense Amount Color": "支出金額の色", "Income Amount Color": "収入金額の色", "Show Account Balance": "口座残高を表示", diff --git a/src/locales/ru.json b/src/locales/ru.json index 6839bba3..a3ed10da 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1826,6 +1826,13 @@ "Currency Unit": "Единица валюты", "Currency Name": "Название валюты", "Currency Symbol": "Символ валюты", + "Geographic Location Format": "Geographic Location Format", + "Latitude Longitude D.D°": "Latitude Longitude D.D°", + "Longitude Latitude D.D°": "Longitude Latitude D.D°", + "Latitude Longitude D°M.M'": "Latitude Longitude D°M.M'", + "Longitude Latitude D°M.M'": "Longitude Latitude D°M.M'", + "Latitude Longitude D°M'S\"": "Latitude Longitude D°M'S\"", + "Longitude Latitude D°M'S\"": "Longitude Latitude D°M'S\"", "Expense Amount Color": "Цвет суммы расхода", "Income Amount Color": "Цвет суммы дохода", "Show Account Balance": "Показать баланс счета", diff --git a/src/locales/uk.json b/src/locales/uk.json index 98d2bbc6..000383c7 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1826,6 +1826,13 @@ "Currency Unit": "Одиниця валюти", "Currency Name": "Назва валюти", "Currency Symbol": "Символ валюти", + "Geographic Location Format": "Geographic Location Format", + "Latitude Longitude D.D°": "Latitude Longitude D.D°", + "Longitude Latitude D.D°": "Longitude Latitude D.D°", + "Latitude Longitude D°M.M'": "Latitude Longitude D°M.M'", + "Longitude Latitude D°M.M'": "Longitude Latitude D°M.M'", + "Latitude Longitude D°M'S\"": "Latitude Longitude D°M'S\"", + "Longitude Latitude D°M'S\"": "Longitude Latitude D°M'S\"", "Expense Amount Color": "Колір суми витрат", "Income Amount Color": "Колір суми доходу", "Show Account Balance": "Показати баланс рахунку", diff --git a/src/locales/vi.json b/src/locales/vi.json index 90aa93f0..b319b657 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -1826,6 +1826,13 @@ "Currency Unit": "Đơn vị tiền tệ", "Currency Name": "Tên tiền tệ", "Currency Symbol": "Ký hiệu tiền tệ", + "Geographic Location Format": "Geographic Location Format", + "Latitude Longitude D.D°": "Latitude Longitude D.D°", + "Longitude Latitude D.D°": "Longitude Latitude D.D°", + "Latitude Longitude D°M.M'": "Latitude Longitude D°M.M'", + "Longitude Latitude D°M.M'": "Longitude Latitude D°M.M'", + "Latitude Longitude D°M'S\"": "Latitude Longitude D°M'S\"", + "Longitude Latitude D°M'S\"": "Longitude Latitude D°M'S\"", "Expense Amount Color": "Màu số tiền chi tiêu", "Income Amount Color": "Màu số tiền thu nhập", "Show Account Balance": "Hiển thị số dư tài khoản", diff --git a/src/locales/zh_Hans.json b/src/locales/zh_Hans.json index e529258c..1dc0f80f 100644 --- a/src/locales/zh_Hans.json +++ b/src/locales/zh_Hans.json @@ -1826,6 +1826,13 @@ "Currency Unit": "货币单位", "Currency Name": "货币名称", "Currency Symbol": "货币符号", + "Geographic Location Format": "地理位置格式", + "Latitude Longitude D.D°": "纬度 经度 D.D°", + "Longitude Latitude D.D°": "经度 纬度 D.D°", + "Latitude Longitude D°M.M'": "纬度 经度 D°M.M'", + "Longitude Latitude D°M.M'": "经度 纬度 D°M.M'", + "Latitude Longitude D°M'S\"": "纬度 经度 D°M'S\"", + "Longitude Latitude D°M'S\"": "经度 纬度 D°M'S\"", "Expense Amount Color": "支出金额颜色", "Income Amount Color": "收入金额颜色", "Show Account Balance": "显示账户余额", diff --git a/src/locales/zh_Hant.json b/src/locales/zh_Hant.json index 3dbf9417..28939f41 100644 --- a/src/locales/zh_Hant.json +++ b/src/locales/zh_Hant.json @@ -1826,6 +1826,13 @@ "Currency Unit": "貨幣單位", "Currency Name": "貨幣名稱", "Currency Symbol": "貨幣符號", + "Geographic Location Format": "地理位置格式", + "Latitude Longitude D.D°": "緯度 經度 D.D°", + "Longitude Latitude D.D°": "經度 緯度 D.D°", + "Latitude Longitude D°M.M'": "緯度 經度 D°M.M'", + "Longitude Latitude D°M.M'": "經度 緯度 D°M.M'", + "Latitude Longitude D°M'S\"": "緯度 經度 D°M'S\"", + "Longitude Latitude D°M'S\"": "經度 緯度 D°M'S\"", "Expense Amount Color": "支出金額顏色", "Income Amount Color": "收入金額顏色", "Show Account Balance": "顯示帳戶餘額", diff --git a/src/models/transaction.ts b/src/models/transaction.ts index c2231b1b..ad2b7182 100644 --- a/src/models/transaction.ts +++ b/src/models/transaction.ts @@ -1,6 +1,6 @@ import type { PartialRecord } from '@/core/base.ts'; import type { YearMonth, StartEndTime } from '@/core/datetime.ts'; -import type { MapPosition } from '@/core/map.ts'; +import { type Coordinate, getNormalizedCoordinate } from '@/core/coordinate.ts'; import { TransactionType } from '@/core/transaction.ts'; import { Account, type AccountInfoResponse } from './account.ts'; @@ -74,7 +74,7 @@ export class Transaction implements TransactionInfoResponse { } - public set geoLocation(value: MapPosition) { + public set geoLocation(value: Coordinate) { this._geoLocation = TransactionGeoLocation.of(value); } @@ -196,8 +196,20 @@ export class Transaction implements TransactionInfoResponse { this._pictures = []; } - public setGeoLocation(geoLocation?: TransactionGeoLocation): void { - this._geoLocation = geoLocation; + public getNormalizedGeoLocation(): Coordinate | undefined { + if (!this._geoLocation) { + return undefined; + } + + return this._geoLocation.toNormalizedCoordinate(); + } + + public setGeoLocation(geoLocation?: Coordinate): void { + if (geoLocation) { + this._geoLocation = TransactionGeoLocation.createNewGeoLocation(geoLocation.latitude, geoLocation.longitude); + } else { + this._geoLocation = undefined; + } } public setLatitudeAndLongitude(latitude: number, longitude: number): void { @@ -228,7 +240,7 @@ export class Transaction implements TransactionInfoResponse { tagIds: this.tagIds, pictureIds: this.getPictureIds(), comment: this.comment, - geoLocation: this.geoLocation, + geoLocation: this.getNormalizedGeoLocation(), clientSessionId: clientSessionId }; } @@ -247,7 +259,7 @@ export class Transaction implements TransactionInfoResponse { tagIds: this.tagIds, pictureIds: this.getPictureIds(), comment: this.comment, - geoLocation: this.geoLocation + geoLocation: this.getNormalizedGeoLocation() }; } @@ -410,8 +422,12 @@ export class TransactionGeoLocation implements TransactionGeoLocationRequest { return new TransactionGeoLocation(latitude, longitude); } - public static of(mapPosition: MapPosition): TransactionGeoLocation { - return new TransactionGeoLocation(mapPosition.latitude, mapPosition.longitude); + public static of(coordinate: Coordinate): TransactionGeoLocation { + return new TransactionGeoLocation(coordinate.latitude, coordinate.longitude); + } + + public toNormalizedCoordinate(): Coordinate { + return getNormalizedCoordinate(this); } } @@ -502,7 +518,7 @@ export interface TransactionListInMonthByPageRequest { readonly keyword: string; } -export type TransactionGeoLocationResponse = MapPosition; +export type TransactionGeoLocationResponse = Coordinate; export interface TransactionInfoResponse { readonly id: string; diff --git a/src/models/user.ts b/src/models/user.ts index 20a64fe3..1c817fba 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -1,6 +1,7 @@ import { LongDateFormat, ShortDateFormat, LongTimeFormat, ShortTimeFormat } from '@/core/datetime.ts'; import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts'; import { CurrencyDisplayType } from '@/core/currency.ts'; +import { CoordinateDisplayType } from '@/core/coordinate.ts'; import { PresetAmountColor } from '@/core/color.ts'; import type { LocalizedPresetCategory } from '@/core/category.ts'; import { TransactionEditScopeType } from '@/core/transaction.ts'; @@ -25,6 +26,7 @@ export class User { public digitGroupingSymbol: number = 0; public digitGrouping: number = 0; public currencyDisplayType: number = 0; + public coordinateDisplayType: number = 0; public expenseAmountColor: number = 0; public incomeAmountColor: number = 0; @@ -51,6 +53,7 @@ export class User { this.digitGroupingSymbol = user.digitGroupingSymbol; this.digitGrouping = user.digitGrouping; this.currencyDisplayType = user.currencyDisplayType; + this.coordinateDisplayType = user.coordinateDisplayType; this.expenseAmountColor = user.expenseAmountColor; this.incomeAmountColor = user.incomeAmountColor; } @@ -87,6 +90,7 @@ export class User { digitGroupingSymbol: this.digitGroupingSymbol, digitGrouping: this.digitGrouping, currencyDisplayType: this.currencyDisplayType, + coordinateDisplayType: this.coordinateDisplayType, expenseAmountColor: this.expenseAmountColor, incomeAmountColor: this.incomeAmountColor }; @@ -104,6 +108,7 @@ export class User { user.digitGroupingSymbol = userInfo.digitGroupingSymbol; user.digitGrouping = userInfo.digitGrouping; user.currencyDisplayType = userInfo.currencyDisplayType; + user.coordinateDisplayType = userInfo.coordinateDisplayType; user.expenseAmountColor = userInfo.expenseAmountColor; user.incomeAmountColor = userInfo.incomeAmountColor; @@ -134,6 +139,7 @@ export interface UserBasicInfo { readonly digitGroupingSymbol: number; readonly digitGrouping: number; readonly currencyDisplayType: number; + readonly coordinateDisplayType: number; readonly expenseAmountColor: number; readonly incomeAmountColor: number; readonly emailVerified: boolean; @@ -184,6 +190,7 @@ export interface UserProfileUpdateRequest { readonly digitGroupingSymbol?: number; readonly digitGrouping?: number; readonly currencyDisplayType?: number; + readonly coordinateDisplayType?: number; readonly expenseAmountColor?: number; readonly incomeAmountColor?: number; } @@ -216,6 +223,7 @@ export const EMPTY_USER_BASIC_INFO: UserBasicInfo = { digitGroupingSymbol: DigitGroupingSymbol.LanguageDefaultType, digitGrouping: DigitGroupingType.LanguageDefaultType, currencyDisplayType: CurrencyDisplayType.Default.type, + coordinateDisplayType: CoordinateDisplayType.Default.type, expenseAmountColor: PresetAmountColor.DefaultExpenseColor.type, incomeAmountColor: PresetAmountColor.DefaultIncomeColor.type, emailVerified: false diff --git a/src/stores/user.ts b/src/stores/user.ts index 0fa7782b..06191ede 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -106,6 +106,11 @@ export const useUserStore = defineStore('user', () => { return userInfo.currencyDisplayType; }); + const currentUserCoordinateDisplayType = computed(() => { + const userInfo = currentUserBasicInfo.value || EMPTY_USER_BASIC_INFO; + return userInfo.coordinateDisplayType; + }); + const currentUserExpenseAmountColor = computed(() => { const userInfo = currentUserBasicInfo.value || EMPTY_USER_BASIC_INFO; return userInfo.expenseAmountColor; @@ -324,6 +329,7 @@ export const useUserStore = defineStore('user', () => { currentUserDigitGroupingSymbol, currentUserDigitGrouping, currentUserCurrencyDisplayType, + currentUserCoordinateDisplayType, currentUserExpenseAmountColor, currentUserIncomeAmountColor, // functions diff --git a/src/views/base/transactions/TransactionEditPageBase.ts b/src/views/base/transactions/TransactionEditPageBase.ts index b5a296c9..33516012 100644 --- a/src/views/base/transactions/TransactionEditPageBase.ts +++ b/src/views/base/transactions/TransactionEditPageBase.ts @@ -92,6 +92,7 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo const defaultCurrency = computed(() => userStore.currentUserDefaultCurrency); const defaultAccountId = computed(() => userStore.currentUserDefaultAccountId); const firstDayOfWeek = computed(() => userStore.currentUserFirstDayOfWeek); + const coordinateDisplayType = computed(() => userStore.currentUserCoordinateDisplayType); const allTimezones = computed(() => getAllTimezones(true)); const allAccounts = computed(() => accountsStore.allPlainAccounts); @@ -416,8 +417,9 @@ export function useTransactionEditPageBase(type: TransactionEditPageType, initMo currentTimezoneOffsetMinutes, showAccountBalance, defaultCurrency, - firstDayOfWeek, defaultAccountId, + firstDayOfWeek, + coordinateDisplayType, allTimezones, allAccounts, allVisibleAccounts, diff --git a/src/views/base/users/UserProfilePageBase.ts b/src/views/base/users/UserProfilePageBase.ts index ccf9dc9d..d99e270e 100644 --- a/src/views/base/users/UserProfilePageBase.ts +++ b/src/views/base/users/UserProfilePageBase.ts @@ -30,6 +30,7 @@ export function useUserProfilePageBase() { getAllDigitGroupingSymbols, getAllDigitGroupingTypes, getAllCurrencyDisplayTypes, + getAllCoordinateDisplayTypes, getAllExpenseAmountColors, getAllIncomeAmountColors, getAllTransactionEditScopeTypes, @@ -63,6 +64,7 @@ export function useUserProfilePageBase() { const allDigitGroupingSymbols = computed(() => getAllDigitGroupingSymbols()); const allDigitGroupingTypes = computed(() => getAllDigitGroupingTypes()); const allCurrencyDisplayTypes = computed(() => getAllCurrencyDisplayTypes()); + const allCoordinateDisplayTypes = computed(() => getAllCoordinateDisplayTypes()); const allExpenseAmountColorTypes = computed(() => getAllExpenseAmountColors()); const allIncomeAmountColorTypes = computed(() => getAllIncomeAmountColors()); const allTransactionEditScopeTypes = computed(() => getAllTransactionEditScopeTypes()); @@ -106,6 +108,7 @@ export function useUserProfilePageBase() { newProfile.value.digitGroupingSymbol === oldProfile.value.digitGroupingSymbol && newProfile.value.digitGrouping === oldProfile.value.digitGrouping && newProfile.value.currencyDisplayType === oldProfile.value.currencyDisplayType && + newProfile.value.coordinateDisplayType === oldProfile.value.coordinateDisplayType && newProfile.value.expenseAmountColor === oldProfile.value.expenseAmountColor && newProfile.value.incomeAmountColor === oldProfile.value.incomeAmountColor) { return 'Nothing has been modified'; @@ -195,6 +198,7 @@ export function useUserProfilePageBase() { allDigitGroupingSymbols, allDigitGroupingTypes, allCurrencyDisplayTypes, + allCoordinateDisplayTypes, allExpenseAmountColorTypes, allIncomeAmountColorTypes, allTransactionEditScopeTypes, diff --git a/src/views/desktop/transactions/import/ImportDialog.vue b/src/views/desktop/transactions/import/ImportDialog.vue index 0f943f97..6ceeb738 100644 --- a/src/views/desktop/transactions/import/ImportDialog.vue +++ b/src/views/desktop/transactions/import/ImportDialog.vue @@ -690,7 +690,7 @@