link edit refactor
This commit is contained in:
parent
5593d59279
commit
3a068f57dc
2 changed files with 142 additions and 72 deletions
|
|
@ -6,6 +6,7 @@ import PeriodSelector from '@/components/PeriodSelector.vue'
|
|||
import ChartBlock from '@/components/ChartBlock.vue'
|
||||
import { useChartsApi } from '@/api/charts.js'
|
||||
import EditLink from '@/views/DetailsView/EditLink.vue'
|
||||
import CopyToClipboard from '@/components/CopyToClipboard.vue'
|
||||
|
||||
const { getMerchDetails, deleteMerch } = useMerchApi()
|
||||
const { getDistinctPrices } = useChartsApi()
|
||||
|
|
@ -17,6 +18,11 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
const editing = ref({
|
||||
surugaya: false,
|
||||
mandarake: false,
|
||||
})
|
||||
|
||||
const merchDetails = ref(null)
|
||||
const loading = ref(true)
|
||||
const error = ref(null)
|
||||
|
|
@ -76,11 +82,15 @@ const fetchPrices = async (days = 7) => {
|
|||
}
|
||||
}
|
||||
|
||||
function handleLinkUpdate(origin, newLink) {
|
||||
merchDetails.value[`origin_${origin}`].link = newLink
|
||||
editing.value[origin] = false
|
||||
}
|
||||
|
||||
function handleSelectDays(days) {
|
||||
fetchPrices(days)
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
fetchMerch()
|
||||
fetchPrices(7)
|
||||
|
|
@ -102,20 +112,71 @@ onMounted(() => {
|
|||
<ChartBlock :charts-data="prices" />
|
||||
</div>
|
||||
|
||||
<n-divider title-placement="left">Surugaya</n-divider>
|
||||
<!-- Surugaya -->
|
||||
<CopyToClipboard :text="merchDetails.origin_surugaya?.link">
|
||||
<n-divider title-placement="left">Surugaya</n-divider>
|
||||
</CopyToClipboard>
|
||||
<div v-if="!editing.surugaya">
|
||||
<template v-if="merchDetails.origin_surugaya?.link">
|
||||
<a
|
||||
:href="merchDetails.origin_surugaya.link"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="default-color"
|
||||
>
|
||||
{{ merchDetails.origin_surugaya.link }}
|
||||
</a>
|
||||
<n-button type="primary" style="margin-left: 8px" @click="editing.surugaya = true">
|
||||
Edit link
|
||||
</n-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="default-color underline link-like-text" @click="editing.surugaya = true">Add link</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<EditLink
|
||||
v-else
|
||||
:merch-uuid="merch_uuid"
|
||||
origin="surugaya"
|
||||
:name="merchDetails.name"
|
||||
v-model="merchDetails.origin_surugaya.link" />
|
||||
:model-value="merchDetails.origin_surugaya?.link || ''"
|
||||
@update:model-value="handleLinkUpdate('surugaya', $event)"
|
||||
@cancel-edit="editing.surugaya = false"
|
||||
/>
|
||||
|
||||
<!-- Mandarake -->
|
||||
<CopyToClipboard :text="merchDetails.origin_mandarake?.link">
|
||||
<n-divider title-placement="left">Mandarake</n-divider>
|
||||
</CopyToClipboard>
|
||||
<div v-if="!editing.mandarake">
|
||||
<template v-if="merchDetails.origin_mandarake?.link">
|
||||
<a
|
||||
:href="merchDetails.origin_mandarake.link"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="default-color"
|
||||
>
|
||||
{{ merchDetails.origin_mandarake.link }}
|
||||
</a>
|
||||
<n-button type="primary" style="margin-left: 8px" @click="editing.mandarake = true">
|
||||
Edit link
|
||||
</n-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="default-color underline link-like-text" @click="editing.mandarake = true">Add link</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<n-divider title-placement="left">Mandarake</n-divider>
|
||||
<EditLink
|
||||
v-else
|
||||
:merch-uuid="merch_uuid"
|
||||
origin="mandarake"
|
||||
:name="merchDetails.name"
|
||||
v-model="merchDetails.origin_mandarake.link" />
|
||||
|
||||
:model-value="merchDetails.origin_mandarake?.link || ''"
|
||||
@update:model-value="handleLinkUpdate('mandarake', $event)"
|
||||
@cancel-edit="editing.mandarake = false"
|
||||
/>
|
||||
</n-card>
|
||||
<div v-else>Not found</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { ref, nextTick, watch, computed } from 'vue'
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { useMerchApi } from '@/api/merch.js'
|
||||
|
||||
const { updateMerch } = useMerchApi()
|
||||
|
|
@ -7,27 +7,19 @@ const { updateMerch } = useMerchApi()
|
|||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
required: true,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: 'Add link'
|
||||
default: 'Enter link here...',
|
||||
},
|
||||
merchUuid: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
origin: {
|
||||
type: String,
|
||||
|
|
@ -35,32 +27,31 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits(['update:modelValue', 'cancel-edit'])
|
||||
|
||||
const isEditing = ref(false)
|
||||
const tempValue = ref('')
|
||||
const inputRef = ref(null)
|
||||
const loading = ref(false)
|
||||
|
||||
const displayValue = computed(() => {
|
||||
const val = props.modelValue.trim()
|
||||
return val === '' || val == null ? props.emptyText : props.modelValue
|
||||
})
|
||||
tempValue.value = props.modelValue
|
||||
|
||||
const startEditing = () => {
|
||||
tempValue.value = props.modelValue
|
||||
isEditing.value = true
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus?.()
|
||||
})
|
||||
}
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus?.()
|
||||
})
|
||||
|
||||
const save = async () => {
|
||||
const newValue = tempValue.value.trim()
|
||||
if (newValue !== props.modelValue) {
|
||||
emit('update:modelValue', newValue)
|
||||
|
||||
if (newValue === '') {
|
||||
emit('cancel-edit')
|
||||
return
|
||||
}
|
||||
isEditing.value = false
|
||||
|
||||
if (newValue === props.modelValue.trim()) {
|
||||
emit('cancel-edit')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
|
|
@ -68,7 +59,7 @@ const save = async () => {
|
|||
merch_uuid: props.merchUuid,
|
||||
name: props.name,
|
||||
origin: props.origin,
|
||||
link: newValue
|
||||
link: newValue,
|
||||
})
|
||||
|
||||
emit('update:modelValue', newValue)
|
||||
|
|
@ -76,63 +67,81 @@ const save = async () => {
|
|||
console.error('Update link error:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
isEditing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const showModal = ref(false)
|
||||
const clearLink = async () => {
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const confirmClearLink = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
await updateMerch({
|
||||
merch_uuid: props.merchUuid,
|
||||
name: props.name,
|
||||
origin: props.origin,
|
||||
link: '',
|
||||
})
|
||||
|
||||
emit('update:modelValue', '')
|
||||
} catch (error) {
|
||||
console.error('Update link error:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
showModal.value = false
|
||||
cancel()
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
isEditing.value = false
|
||||
emit('cancel-edit')
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if (isEditing.value) {
|
||||
tempValue.value = newVal
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<span v-if="!isEditing" @click="startEditing" class="editable-text">
|
||||
{{ displayValue }}
|
||||
</span>
|
||||
<div v-else class="editing-area">
|
||||
<div class="editing-area">
|
||||
<n-input
|
||||
v-model:value="tempValue"
|
||||
type="text"
|
||||
size="small"
|
||||
type="textarea"
|
||||
size="large"
|
||||
ref="inputRef"
|
||||
@keyup.enter="save"
|
||||
@keyup.esc="cancel"
|
||||
:placeholder="placeholder || 'Enter link here...'"
|
||||
:placeholder="placeholder"
|
||||
:loading="loading"
|
||||
/>
|
||||
<n-button size="small" type="primary" @click="save">✓</n-button>
|
||||
<n-button size="small" @click="cancel">✕</n-button>
|
||||
<n-button size="small" type="primary" :loading="loading" @click="save">Save</n-button>
|
||||
<n-button size="small" @click="cancel">Cancel</n-button>
|
||||
</div>
|
||||
<div class="center-button-container">
|
||||
<n-button type="error" class="center-button" @click="clearLink">Clear link</n-button>
|
||||
</div>
|
||||
|
||||
<n-modal v-model:show="showModal" preset="dialog" title="Confirmation">
|
||||
<template #default>
|
||||
<p>Confirm clear link</p>
|
||||
</template>
|
||||
<template #action>
|
||||
<n-button @click="confirmClearLink" type="error">Clear</n-button>
|
||||
<n-button @click="showModal = false">Cancel</n-button>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.editable-text {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
color: #18a058;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s;
|
||||
min-height: 1.2em;
|
||||
display: inline-block;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.editable-text:hover {
|
||||
background-color: #f0f9ff;
|
||||
}
|
||||
|
||||
.editing-area {
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.editing-area :deep(.n-input) {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue