浏览代码

Render and select address from dropdown

master
父节点
当前提交
1d7163f5ae
共有 8 个文件被更改,包括 132 次插入13 次删除
  1. +32
    -0
      components/dropdown.vue
  2. +60
    -1
      components/settings.vue
  3. +0
    -9
      config.default.go
  4. +1
    -0
      migrations/0_29092022_setup_tables.sql
  5. +34
    -0
      package-lock.json
  6. +1
    -0
      package.json
  7. +1
    -1
      skouter.go
  8. +3
    -2
      webpack.config.js

+ 32
- 0
components/dropdown.vue 查看文件

@@ -0,0 +1,32 @@
<template>
<div class="entries">
<span class="entry" v-for="entry in entries"
@click="$emit('select', entry.value)"
>{{entry.text}}</span>
</div>
</template>

<style scoped>
.entries {
display: flex;
flex-flow: column;
position: absolute;
left: 0;
background: var(--secondary-bg);
width: 100%;
margin-top: 5px;
z-index: 3;
border: 1px solid black;
}
.entry {
border-bottom: 1px solid var(--text);
padding: 5px;
cursor: pointer;
}
</style>

<script setup>
const props = defineProps(['entries'])
const emits = defineEmits(['select'])
</script>

+ 60
- 1
components/settings.vue 查看文件

@@ -38,6 +38,16 @@
<option value="Canada">Canada</option> <option value="Canada">Canada</option>
</select> </select>


<div class="address-entry">
<label for="">Address</label>
<input type="text" @input="searchLocation" :value="address.full">
<dropdown v-if="addresses && addresses.length"
:entries="addresses.map(a => ({text: a.full_address, value: a}))"
@select="setAddress"
>
</dropdown>
</div>

<button @click="saveProfile">Save</button> <button @click="saveProfile">Save</button>
</section> </section>


@@ -67,6 +77,7 @@
<script setup> <script setup>
import { ref, watch, onMounted } from "vue" import { ref, watch, onMounted } from "vue"
import Dialog from "./dialog.vue" import Dialog from "./dialog.vue"
import Dropdown from "./dropdown.vue"


let avatar = ref(null) // the canvas element let avatar = ref(null) // the canvas element
let letterhead = ref(null) // the canvas element let letterhead = ref(null) // the canvas element
@@ -78,6 +89,9 @@ let passError = ref('')
let oldPass = ref('') let oldPass = ref('')
let newPass = ref('') let newPass = ref('')
let confirmPass = ref('') let confirmPass = ref('')
const locationsId = ref(null)
const addresses = ref([])
const address = ref({})
const props = defineProps(['user', 'token']) const props = defineProps(['user', 'token'])
const emit = defineEmits(['updateAvatar', 'updateLetterhead']) const emit = defineEmits(['updateAvatar', 'updateLetterhead'])
let user = Object.assign({}, props.user) let user = Object.assign({}, props.user)
@@ -176,8 +190,19 @@ function changeLetterhead(blob) {
}) })
} }


function setAddress(a) {
addresses.value = null
address.value = {
full: a.full_address,
street: a.address,
city: a.context.place.name,
region: a.context.region.name,
country: a.context.country.name,
zip: a.context.postcode.name,
}
}

function saveProfile() { function saveProfile() {
console.log(user.firstName)
fetch(`/api/user`, fetch(`/api/user`,
{method: 'PATCH', {method: 'PATCH',
body: JSON.stringify(user), body: JSON.stringify(user),
@@ -216,6 +241,31 @@ function changePassword(f) {
}) })
} }


function searchLocation(e) {
address.value.full = e.target.value
clearTimeout(locationsId.value)
locationsId.value = setTimeout(getLocations, 1000, e)
}

function getLocations(e) {
let prefix = "https://api.mapbox.com/search/searchbox/v1/suggest?"
let search = e.target.value
let key = encodeURIComponent(process.env.MAPBOX_API_KEY)
if (!search) return
fetch(`${prefix}q=${search}&proximity=ip&access_token=${key}&session_token=1`
).then(resp => resp.json()).then(entries => {
if (!entries?.suggestions) {
addresses.value = null
return
}
addresses.value = entries.suggestions.filter(e => e.full_address)
})
}


watch(props.user, (u) => { watch(props.user, (u) => {
if (props.user.avatar) { if (props.user.avatar) {
changeAvatar(props.user.avatar) changeAvatar(props.user.avatar)
@@ -226,4 +276,13 @@ watch(props.user, (u) => {
} }
}, {immediate: true}) }, {immediate: true})


onMounted(() => {
})

</script> </script>

<style scoped>
div.address-entry input {
width: 100%;
}
</style>

+ 0
- 9
config.default.go 查看文件

@@ -1,9 +0,0 @@
/*
package main

var config = map[string]string {
"dbUsername": "",
"dbPassword": "",
}
*/
package main

+ 1
- 0
migrations/0_29092022_setup_tables.sql 查看文件

@@ -2,6 +2,7 @@


CREATE TABLE address ( CREATE TABLE address (
id INT AUTO_INCREMENT, id INT AUTO_INCREMENT,
full VARCHAR(200) UNIQUE NOT NULL,
street VARCHAR(40) UNIQUE NOT NULL, street VARCHAR(40) UNIQUE NOT NULL,
city VARCHAR(40) UNIQUE NOT NULL, city VARCHAR(40) UNIQUE NOT NULL,
region VARCHAR(40) UNIQUE NOT NULL, region VARCHAR(40) UNIQUE NOT NULL,


+ 34
- 0
package-lock.json 查看文件

@@ -11,6 +11,7 @@
}, },
"devDependencies": { "devDependencies": {
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"dotenv-webpack": "^8.0.1",
"vue-loader": "^17.0.1", "vue-loader": "^17.0.1",
"vue-template-compiler": "^2.7.13", "vue-template-compiler": "^2.7.13",
"webpack": "^5.74.0", "webpack": "^5.74.0",
@@ -1277,6 +1278,39 @@
"integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==",
"optional": true "optional": true
}, },
"node_modules/dotenv": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
"integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
"dev": true,
"engines": {
"node": ">=10"
}
},
"node_modules/dotenv-defaults": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
"integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
"dev": true,
"dependencies": {
"dotenv": "^8.2.0"
}
},
"node_modules/dotenv-webpack": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz",
"integrity": "sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w==",
"dev": true,
"dependencies": {
"dotenv-defaults": "^2.0.2"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"webpack": "^4 || ^5"
}
},
"node_modules/ee-first": { "node_modules/ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",


+ 1
- 0
package.json 查看文件

@@ -9,6 +9,7 @@
}, },
"devDependencies": { "devDependencies": {
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"dotenv-webpack": "^8.0.1",
"vue-loader": "^17.0.1", "vue-loader": "^17.0.1",
"vue-template-compiler": "^2.7.13", "vue-template-compiler": "^2.7.13",
"webpack": "^5.74.0", "webpack": "^5.74.0",


+ 1
- 1
skouter.go 查看文件

@@ -29,6 +29,7 @@ import (


type Address struct { type Address struct {
Id int `json:"id"` Id int `json:"id"`
Full string `json:"full"`
Street string `json:"street"` Street string `json:"street"`
City string `json:"city"` City string `json:"city"`
Region string `json:"region"` Region string `json:"region"`
@@ -1017,7 +1018,6 @@ func changePassword(w http.ResponseWriter, db *sql.DB, r *http.Request) {
claim, err := getClaims(r) claim, err := getClaims(r)
err = json.NewDecoder(r.Body).Decode(&pass) err = json.NewDecoder(r.Body).Decode(&pass)
if err != nil { http.Error(w, "Bad fields.", 422); return } if err != nil { http.Error(w, "Bad fields.", 422); return }
fmt.Println(pass)
if checkPassword(db, claim.Id, pass.Old) { if checkPassword(db, claim.Id, pass.Old) {
err = setPassword(db, claim.Id, pass.New) err = setPassword(db, claim.Id, pass.New)


+ 3
- 2
webpack.config.js 查看文件

@@ -1,5 +1,6 @@
const { VueLoaderPlugin } = require('vue-loader') const { VueLoaderPlugin } = require('vue-loader')
const path = require('path');
const path = require('path')
const Dotenv = require('dotenv-webpack')


module.exports = { module.exports = {
entry: './main.js', entry: './main.js',
@@ -23,5 +24,5 @@ module.exports = {
} }
}, },
// Required for also applying rules to sections of SFC // Required for also applying rules to sections of SFC
plugins: [new VueLoaderPlugin()],
plugins: [new VueLoaderPlugin(), new Dotenv({systemVars: true})],
}; };

正在加载...
取消
保存