ADD: Pinia
This commit is contained in:
68
package-lock.json
generated
68
package-lock.json
generated
@@ -21,6 +21,7 @@
|
|||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
"lucide-vue-next": "^0.309.0",
|
"lucide-vue-next": "^0.309.0",
|
||||||
|
"pinia": "^2.1.7",
|
||||||
"radix-vue": "^1.3.0",
|
"radix-vue": "^1.3.0",
|
||||||
"tailwind-merge": "^2.2.0",
|
"tailwind-merge": "^2.2.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
@@ -8304,6 +8305,56 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pinia": {
|
||||||
|
"version": "2.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz",
|
||||||
|
"integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.5.0",
|
||||||
|
"vue-demi": ">=0.14.5"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.4.0",
|
||||||
|
"typescript": ">=4.4.4",
|
||||||
|
"vue": "^2.6.14 || ^3.3.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pinia/node_modules/vue-demi": {
|
||||||
|
"version": "0.14.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz",
|
||||||
|
"integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pirates": {
|
"node_modules/pirates": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
||||||
@@ -16738,6 +16789,23 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
|
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
|
||||||
},
|
},
|
||||||
|
"pinia": {
|
||||||
|
"version": "2.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz",
|
||||||
|
"integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
|
||||||
|
"requires": {
|
||||||
|
"@vue/devtools-api": "^6.5.0",
|
||||||
|
"vue-demi": ">=0.14.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue-demi": {
|
||||||
|
"version": "0.14.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz",
|
||||||
|
"integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==",
|
||||||
|
"requires": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"pirates": {
|
"pirates": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
"lucide-vue-next": "^0.309.0",
|
"lucide-vue-next": "^0.309.0",
|
||||||
|
"pinia": "^2.1.7",
|
||||||
"radix-vue": "^1.3.0",
|
"radix-vue": "^1.3.0",
|
||||||
"tailwind-merge": "^2.2.0",
|
"tailwind-merge": "^2.2.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
|||||||
@@ -7,15 +7,21 @@ import ConfigSelect from '@/components/config/ConfigSelect.vue'
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import ConfigSection from '@/components/config/ConfigSection.vue'
|
import ConfigSection from '@/components/config/ConfigSection.vue'
|
||||||
import { Bolt } from 'lucide-vue-next'
|
import { Bolt } from 'lucide-vue-next'
|
||||||
|
import { useStore } from '@/store'
|
||||||
|
|
||||||
const currentConfigPage = ref('profile_settings')
|
const currentConfigPage = ref('profile_settings')
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
store.fetchProfiles()
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<main class="select-none w-screen h-screen flex flex-col">
|
<main class="select-none w-screen h-screen flex flex-col">
|
||||||
<Navbar class="flex-none" />
|
<Navbar class="flex-none" />
|
||||||
<div class="flex-1 min-h-0 flex flex-row">
|
<div class="flex-1 min-h-0 flex flex-row">
|
||||||
<ProfileManager class="basis-1/3 min-w-80 flex-1 flex flex-col border-solid border-0 border-r bg-zinc-900 bg-opacity-30" />
|
<ProfileManager
|
||||||
|
class="basis-1/3 min-w-80 flex-1 flex flex-col border-solid border-0 border-r bg-zinc-900 bg-opacity-30" />
|
||||||
<div class="basis-1/3 flex-1 flex flex-col border-solid border-0 border-r">
|
<div class="basis-1/3 flex-1 flex flex-col border-solid border-0 border-r">
|
||||||
<DevicePreview class="flex-col flex flex-none" />
|
<DevicePreview class="flex-col flex flex-none" />
|
||||||
<ConfigSection class="flex-none" :title="$t('config_options.title')" :foldable="false" :icon-component="Bolt" />
|
<ConfigSection class="flex-none" :title="$t('config_options.title')" :foldable="false" :icon-component="Bolt" />
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ import ConfigSection from '@/components/config/ConfigSection.vue'
|
|||||||
import PaletteInput from '@/components/config/PaletteInput.vue'
|
import PaletteInput from '@/components/config/PaletteInput.vue'
|
||||||
import Color from 'color'
|
import Color from 'color'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { store } from '@/store.js'
|
import { useStore } from '@/store.js'
|
||||||
import { Slider } from '@/components/ui/slider/index.js'
|
import { Slider } from '@/components/ui/slider/index.js'
|
||||||
|
|
||||||
const ledConfig = computed(() => store.currentProfile.ledConfig)
|
const store = useStore()
|
||||||
|
|
||||||
|
const ledConfig = computed(() => store.selectedProfile.ledConfig)
|
||||||
|
|
||||||
const brightnessSliderModel = computed({
|
const brightnessSliderModel = computed({
|
||||||
get: () => [ledConfig.value.ledBrightness],
|
get: () => [ledConfig.value.ledBrightness],
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
class="w-full px-4 h-20 flex items-center">
|
class="w-full px-4 h-20 flex items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-lg">
|
<h1 class="text-lg">
|
||||||
{{ $t(`profiles.title`) }}<span class="text-sm text-zinc-600"> ({{ profiles.length }}/{{ maxProfiles
|
{{ $t(`profiles.title`) }}<span class="text-sm text-zinc-600"> ({{ store.profiles.length }}/{{ maxProfiles
|
||||||
}})</span>
|
}})</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-xs text-muted-foreground">
|
<p class="text-xs text-muted-foreground">
|
||||||
@@ -53,10 +53,10 @@
|
|||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<ProfileButton
|
<ProfileButton
|
||||||
v-for="(profile, index) in tagProfiles" :key="profile.id" v-model="tagProfiles[index]"
|
v-for="(profile, index) in tagProfiles" :key="profile.id" v-model="tagProfiles[index]"
|
||||||
:selected="currentProfileId===profile.id"
|
:selected="store.selectedProfile.id === profile.id"
|
||||||
@select="currentProfileId=profile.id"
|
@select="store.selectProfile(profile.id)"
|
||||||
@duplicate="duplicateProfile(profile.id)"
|
@duplicate="store.duplicateProfile(profile.id)"
|
||||||
@delete="deleteProfile(profile.id)" />
|
@delete="store.deleteProfile(profile.id)" />
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,28 +69,21 @@ import { ChevronRight, Plus, Search } from 'lucide-vue-next'
|
|||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
||||||
import ScrambleText from '@/components/effects/ScrambleText.vue'
|
import ScrambleText from '@/components/effects/ScrambleText.vue'
|
||||||
import { store } from '@/store.js'
|
import { useStore } from '@/store.js'
|
||||||
import ProfileButton from '@/components/profile/ProfileButton.vue'
|
import ProfileButton from '@/components/profile/ProfileButton.vue'
|
||||||
|
|
||||||
const maxProfiles = 32
|
const maxProfiles = 32
|
||||||
|
|
||||||
const profiles = computed({
|
const store = useStore()
|
||||||
get: () => store.device.profiles,
|
|
||||||
set: val => store.device.profiles = val,
|
|
||||||
})
|
|
||||||
const currentProfileId = computed({
|
|
||||||
get: () => store.currentProfileId,
|
|
||||||
set: val => store.currentProfileId = val,
|
|
||||||
})
|
|
||||||
const filter = ref('')
|
const filter = ref('')
|
||||||
const collapse = ref({})
|
const collapse = ref({})
|
||||||
|
|
||||||
const filteredProfiles = computed(() => {
|
const filteredProfiles = computed(() => {
|
||||||
if (!filter.value) {
|
if (!filter.value) {
|
||||||
return profiles.value
|
return store.profiles
|
||||||
}
|
}
|
||||||
const filterLower = filter.value.toLowerCase()
|
const filterLower = filter.value.toLowerCase()
|
||||||
return profiles.value.filter(profile => {
|
return store.profiles.filter(profile => {
|
||||||
const nameLower = profile.name.toLowerCase()
|
const nameLower = profile.name.toLowerCase()
|
||||||
const idLower = profile.id.toLowerCase()
|
const idLower = profile.id.toLowerCase()
|
||||||
const tagLower = profile.profileTag.toLowerCase()
|
const tagLower = profile.profileTag.toLowerCase()
|
||||||
@@ -98,47 +91,6 @@ const filteredProfiles = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function newProfileId(originalId = '') {
|
|
||||||
let id = originalId
|
|
||||||
if (originalId) {
|
|
||||||
do {
|
|
||||||
id = Math.floor((parseInt(id) + 1) % 9999).toString().padStart(4, '0')
|
|
||||||
} while (store.profileIds.includes(id))
|
|
||||||
} else {
|
|
||||||
do {
|
|
||||||
id = Math.floor(Math.random() * 9999).toString().padStart(4, '0')
|
|
||||||
} while (store.profileIds.includes(id))
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
function newProfileName(originalName) {
|
|
||||||
let name = originalName
|
|
||||||
let i = 1
|
|
||||||
while (profiles.value.find(p => p.name === name)) {
|
|
||||||
name = `${originalName} (${i++})`
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
const duplicateProfile = (id) => {
|
|
||||||
const profile = profiles.value.find(p => p.id === id)
|
|
||||||
const profileIndex = profiles.value.indexOf(profile)
|
|
||||||
const newProfile = JSON.parse(JSON.stringify(profile))
|
|
||||||
newProfile.id = newProfileId(id)
|
|
||||||
newProfile.name = newProfileName(profile.name)
|
|
||||||
profiles.value.splice(profileIndex + 1, 0, newProfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteProfile = (id) => {
|
|
||||||
const profile = profiles.value.find(p => p.id === id)
|
|
||||||
const profileIndex = profiles.value.indexOf(profile)
|
|
||||||
profiles.value.splice(profileIndex, 1)
|
|
||||||
if (currentProfileId.value === id && profiles.value.length > 0) {
|
|
||||||
currentProfileId.value = profiles.value[0].id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredProfilesByTag = computed(() => {
|
const filteredProfilesByTag = computed(() => {
|
||||||
const map = new Map()
|
const map = new Map()
|
||||||
filteredProfiles.value.forEach(profile => {
|
filteredProfiles.value.forEach(profile => {
|
||||||
@@ -151,11 +103,6 @@ const filteredProfilesByTag = computed(() => {
|
|||||||
return map
|
return map
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(profiles, () => {
|
|
||||||
if (!currentProfileId.value && profiles.value.length > 0)
|
|
||||||
currentProfileId.value = profiles.value[0].id
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
[data-state=open] > .chevrot {
|
[data-state=open] > .chevrot {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue'
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import en from '@/lang/en.json'
|
import en from '@/lang/en.json'
|
||||||
@@ -35,41 +35,16 @@ import en from '@/lang/en.json'
|
|||||||
import './assets/main.css'
|
import './assets/main.css'
|
||||||
import './assets/index.css'
|
import './assets/index.css'
|
||||||
|
|
||||||
import { store } from '@/store.js'
|
import { pinia } from '@/store.js'
|
||||||
import Ajv from 'ajv'
|
|
||||||
import schema from '@/data/profileSchema.json'
|
|
||||||
import Axios from 'axios'
|
|
||||||
|
|
||||||
const ajv = new Ajv()
|
|
||||||
|
|
||||||
Axios.get('http://localhost:3001/profiles').then((res) => {
|
|
||||||
const profiles = res.data
|
|
||||||
console.log(profiles)
|
|
||||||
const ids = new Set()
|
|
||||||
const validate = ajv.compile(schema)
|
|
||||||
store.device.profiles = profiles.filter((profile) => {
|
|
||||||
if (!validate(profile)) {
|
|
||||||
console.error('Failed to validate profile: ' + profile.name, validate.errors)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (ids.has(profile.id)) {
|
|
||||||
console.error('Duplicate profile id: ' + profile.id + ' for profile: ' + profile.name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
ids.add(profile.id)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}).catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create VueI18n instance with locales loaded from /lang directory
|
// Create VueI18n instance with locales loaded from /lang directory
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
fallbackLocale: 'en',
|
fallbackLocale: 'en',
|
||||||
messages: { en: en },
|
messages: { en: en },
|
||||||
});
|
})
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App)
|
||||||
|
app.use(pinia)
|
||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
app.mount('#app');
|
app.mount('#app')
|
||||||
125
src/store.js
125
src/store.js
@@ -1,16 +1,113 @@
|
|||||||
import { computed, reactive } from 'vue'
|
// import { computed, reactive } from 'vue'
|
||||||
|
//
|
||||||
|
// export const store = reactive({
|
||||||
|
// device: {
|
||||||
|
// profiles: [],
|
||||||
|
// activeProfileId: null,
|
||||||
|
// activeProfile: computed(() => {
|
||||||
|
// return store.device.profiles.find(p => p.id === store.device.activeProfileId)
|
||||||
|
// }),
|
||||||
|
// },
|
||||||
|
// currentProfileId: null,
|
||||||
|
// currentProfile: computed(() => {
|
||||||
|
// return store.device.profiles.find(p => p.id === store.currentProfileId)
|
||||||
|
// }),
|
||||||
|
// profileIds: computed(() => store.device.profiles.map(p => p.id)),
|
||||||
|
// })
|
||||||
|
|
||||||
export const store = reactive({
|
import { createPinia, defineStore } from 'pinia'
|
||||||
device: {
|
import Axios from 'axios'
|
||||||
profiles: [],
|
import schema from '@/data/profileSchema.json'
|
||||||
activeProfileId: null,
|
import Ajv from 'ajv'
|
||||||
activeProfile: computed(() => {
|
|
||||||
return store.device.profiles.find(p => p.id === store.device.activeProfileId)
|
const ajv = new Ajv()
|
||||||
}),
|
|
||||||
|
export const useStore = defineStore('main', {
|
||||||
|
// arrow function recommended for full type inference
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
profiles: [],
|
||||||
|
selectedProfileId: null,
|
||||||
|
connected: false,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
currentProfileId: null,
|
getters: {
|
||||||
currentProfile: computed(() => {
|
profileIds: (state) => state.profiles.map(p => p.id),
|
||||||
return store.device.profiles.find(p => p.id === store.currentProfileId)
|
selectedProfile: (state) => state.profiles.find(p => p.id === state.selectedProfileId),
|
||||||
}),
|
},
|
||||||
profileIds: computed(() => store.device.profiles.map(p => p.id)),
|
actions: {
|
||||||
})
|
selectProfile(id) {
|
||||||
|
if (!this.profileIds.includes(id)) return false
|
||||||
|
this.selectedProfileId = id
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
duplicateProfile(id) {
|
||||||
|
const originalProfile = this.profiles.find(p => p.id === id)
|
||||||
|
const newProfile = JSON.parse(JSON.stringify(originalProfile))
|
||||||
|
newProfile.id = this.newProfileId(originalProfile.id)
|
||||||
|
newProfile.name = this.newProfileName(originalProfile.name)
|
||||||
|
this.profiles.push(newProfile)
|
||||||
|
this.selectedProfileId = newProfile.id
|
||||||
|
return newProfile.id
|
||||||
|
},
|
||||||
|
deleteProfile(id) {
|
||||||
|
const index = this.profiles.findIndex(p => p.id === id)
|
||||||
|
if (index >= 0) {
|
||||||
|
this.profiles.splice(index, 1)
|
||||||
|
if (this.selectedProfileId === id) {
|
||||||
|
this.selectedProfileId = this.profiles[0]?.id || null
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
fetchProfiles() {
|
||||||
|
Axios.get('http://localhost:3001/profiles').then((res) => {
|
||||||
|
const profiles = res.data
|
||||||
|
console.log(profiles)
|
||||||
|
const ids = new Set()
|
||||||
|
const validate = ajv.compile(schema)
|
||||||
|
this.$patch({
|
||||||
|
profiles: profiles.filter((profile) => {
|
||||||
|
if (!validate(profile)) {
|
||||||
|
console.error('Failed to validate profile: ' + profile.name, validate.errors)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (ids.has(profile.id)) {
|
||||||
|
console.error('Duplicate profile id: ' + profile.id + ' for profile: ' + profile.name)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ids.add(profile.id)
|
||||||
|
return true
|
||||||
|
}),
|
||||||
|
selectedProfileId: profiles[0]?.id || null,
|
||||||
|
})
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
newProfileName(originalName = '') {
|
||||||
|
let name = originalName
|
||||||
|
let i = 1
|
||||||
|
while (this.profiles.find(p => p.name === name)) {
|
||||||
|
name = `${originalName} (${i++})`
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
},
|
||||||
|
newProfileId(originalId = '') {
|
||||||
|
let id = originalId
|
||||||
|
if (originalId) {
|
||||||
|
do {
|
||||||
|
id = Math.floor((parseInt(id) + 1) % 9999).toString().padStart(4, '0')
|
||||||
|
} while (this.profileIds.includes(id))
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
id = Math.floor(Math.random() * 9999).toString().padStart(4, '0')
|
||||||
|
} while (this.profileIds.includes(id))
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const pinia = createPinia()
|
||||||
Reference in New Issue
Block a user