|
|
@@ -119,20 +119,64 @@
|
|
|
:model="model"
|
|
|
:on-submit="submit"
|
|
|
@success="table.refresh()"
|
|
|
- style="width: 80%"
|
|
|
+ style="width: 90%"
|
|
|
>
|
|
|
<ElForm label-width="100px" label-position="right">
|
|
|
- <div style="display: flex; align-items: stretch; gap: 20px; height: 500px">
|
|
|
- <div style="flex: 1; display: flex; flex-direction: column; justify-content: center">
|
|
|
- <ElFormItem prop="content" label="助记词内容">
|
|
|
- <ElInput v-model="model.content" placeholder="请输入内容" type="textarea" :rows="15" />
|
|
|
+ <div class="dialog-content">
|
|
|
+ <!-- 左侧输入和展示区域 -->
|
|
|
+ <div class="left-section">
|
|
|
+ <ElFormItem prop="content" label="助记词内容: " class="input-section">
|
|
|
+ <div class="input-wrapper">
|
|
|
+ <ElInput
|
|
|
+ v-model="inputWord"
|
|
|
+ placeholder="输入助记词"
|
|
|
+ @input="handleWordInput"
|
|
|
+ @keydown="handleKeyDown"
|
|
|
+ clearable
|
|
|
+ class="word-input"
|
|
|
+ />
|
|
|
+ <div class="suggestions" v-if="suggestions.length > 0">
|
|
|
+ <div
|
|
|
+ v-for="(word, index) in suggestions"
|
|
|
+ :key="word"
|
|
|
+ class="suggestion-item"
|
|
|
+ :class="{ 'suggestion-item-active': index === selectedIndex }"
|
|
|
+ @click="selectWord(word)"
|
|
|
+ >
|
|
|
+ {{ word }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</ElFormItem>
|
|
|
+ <div class="selected-words-container">
|
|
|
+ <div
|
|
|
+ v-for="(word, index) in selectedWords"
|
|
|
+ :key="index"
|
|
|
+ class="selected-word-item"
|
|
|
+ >
|
|
|
+ <span>{{ word }}</span>
|
|
|
+ <ElButton
|
|
|
+ type="danger"
|
|
|
+ :icon="X"
|
|
|
+ circle
|
|
|
+ size="small"
|
|
|
+ @click="removeWord(index)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
- <ElDivider direction="vertical" style="height: 100%" />
|
|
|
+ <ElDivider direction="vertical" class="divider" />
|
|
|
|
|
|
- <div style="flex: 1; display: flex; justify-content: center; align-items: center">
|
|
|
- <RImage style="max-width: 100%; height: 100%; object-fit: cover" :src="model.img" fit="cover" />
|
|
|
+ <!-- 右侧图片区域 -->
|
|
|
+ <div class="right-section">
|
|
|
+ <div class="image-container">
|
|
|
+ <RImage
|
|
|
+ :src="model.img"
|
|
|
+ fit="contain"
|
|
|
+ class="preview-image"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</ElForm>
|
|
|
@@ -159,16 +203,17 @@
|
|
|
</ElDialog>
|
|
|
</template>
|
|
|
<script setup>
|
|
|
-import { inject, ref, onMounted } from 'vue'
|
|
|
+import { inject, ref, onMounted, watch } from 'vue'
|
|
|
import PagingTable from '@/components/PagingTable.vue'
|
|
|
import { useTimeFormatter } from '@/utils/formatter'
|
|
|
-import { Check, Edit, Plus, Search, Refresh, Star, StarOff, ClearAll } from '@vicons/tabler'
|
|
|
+import { Check, Edit, Plus, Search, Refresh, Star, StarOff, ClearAll, X } from '@vicons/tabler'
|
|
|
import { ElLoading, ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import EditDialog from '@/components/EditDialog.vue'
|
|
|
import { setupEditDialog } from '@/utils/editDialog'
|
|
|
import { http } from '@/plugins/http'
|
|
|
import { useClipboard } from '@vueuse/core'
|
|
|
import { format } from 'date-fns'
|
|
|
+import { wordlists } from 'bip39'
|
|
|
|
|
|
const query = ref({
|
|
|
width: 50,
|
|
|
@@ -226,6 +271,82 @@ const shortcuts = [
|
|
|
|
|
|
const channelOptions = ref([])
|
|
|
|
|
|
+// 添加新的响应式变量
|
|
|
+const inputWord = ref('')
|
|
|
+const suggestions = ref([])
|
|
|
+const selectedWords = ref([])
|
|
|
+const selectedIndex = ref(-1)
|
|
|
+
|
|
|
+// 监听model变化,初始化selectedWords
|
|
|
+watch(() => model.value.content, (newVal) => {
|
|
|
+ if (newVal) {
|
|
|
+ selectedWords.value = newVal.split(' ').filter(Boolean)
|
|
|
+ }
|
|
|
+}, { immediate: true })
|
|
|
+
|
|
|
+// 使用BIP39的英文单词列表
|
|
|
+const bip39Words = wordlists.english
|
|
|
+
|
|
|
+// 处理单词输入
|
|
|
+function handleWordInput() {
|
|
|
+ if (!inputWord.value) {
|
|
|
+ suggestions.value = []
|
|
|
+ selectedIndex.value = -1
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const input = inputWord.value.toLowerCase()
|
|
|
+ suggestions.value = bip39Words
|
|
|
+ .filter(word => word.startsWith(input))
|
|
|
+ .slice(0, 10)
|
|
|
+ selectedIndex.value = -1
|
|
|
+}
|
|
|
+
|
|
|
+// 处理键盘事件
|
|
|
+function handleKeyDown(e) {
|
|
|
+ if (suggestions.value.length === 0) return
|
|
|
+
|
|
|
+ switch (e.key) {
|
|
|
+ case 'ArrowDown':
|
|
|
+ e.preventDefault()
|
|
|
+ selectedIndex.value = (selectedIndex.value + 1) % suggestions.value.length
|
|
|
+ scrollToSelected()
|
|
|
+ break
|
|
|
+ case 'ArrowUp':
|
|
|
+ e.preventDefault()
|
|
|
+ selectedIndex.value = (selectedIndex.value - 1 + suggestions.value.length) % suggestions.value.length
|
|
|
+ scrollToSelected()
|
|
|
+ break
|
|
|
+ case 'Enter':
|
|
|
+ e.preventDefault()
|
|
|
+ if (selectedIndex.value >= 0) {
|
|
|
+ selectWord(suggestions.value[selectedIndex.value])
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 选择单词
|
|
|
+function selectWord(word) {
|
|
|
+ if (!selectedWords.value.includes(word)) {
|
|
|
+ selectedWords.value.push(word)
|
|
|
+ updateContent()
|
|
|
+ }
|
|
|
+ inputWord.value = ''
|
|
|
+ suggestions.value = []
|
|
|
+ selectedIndex.value = -1
|
|
|
+}
|
|
|
+
|
|
|
+// 移除单词
|
|
|
+function removeWord(index) {
|
|
|
+ selectedWords.value.splice(index, 1)
|
|
|
+ updateContent()
|
|
|
+}
|
|
|
+
|
|
|
+// 更新内容
|
|
|
+function updateContent() {
|
|
|
+ model.value.content = selectedWords.value.join(' ')
|
|
|
+}
|
|
|
+
|
|
|
onMounted(async () => {
|
|
|
try {
|
|
|
const response = await http.post('/ocrChannel/names')
|
|
|
@@ -364,6 +485,32 @@ async function confirmExport() {
|
|
|
}
|
|
|
exportDialog()
|
|
|
}
|
|
|
+
|
|
|
+// 滚动到选中项
|
|
|
+function scrollToSelected() {
|
|
|
+ const suggestionsEl = document.querySelector('.suggestions')
|
|
|
+ const selectedEl = suggestionsEl?.querySelector('.suggestion-item-active')
|
|
|
+ if (selectedEl) {
|
|
|
+ const containerRect = suggestionsEl.getBoundingClientRect()
|
|
|
+ const selectedRect = selectedEl.getBoundingClientRect()
|
|
|
+ const itemHeight = selectedRect.height
|
|
|
+ const visibleHeight = containerRect.height
|
|
|
+ const scrollTop = suggestionsEl.scrollTop
|
|
|
+ const selectedTop = selectedRect.top - containerRect.top + scrollTop
|
|
|
+
|
|
|
+ // 计算选中项在可视区域中的位置
|
|
|
+ const positionInViewport = selectedTop - scrollTop
|
|
|
+
|
|
|
+ // 如果选中项在中间位置以下,向下滚动
|
|
|
+ if (positionInViewport > visibleHeight / 2) {
|
|
|
+ suggestionsEl.scrollTop = selectedTop - visibleHeight / 2 + itemHeight / 2
|
|
|
+ }
|
|
|
+ // 如果选中项在中间位置以上,向上滚动
|
|
|
+ else if (positionInViewport < visibleHeight / 2 - itemHeight) {
|
|
|
+ suggestionsEl.scrollTop = selectedTop - visibleHeight / 2 + itemHeight / 2
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
@@ -405,7 +552,7 @@ async function confirmExport() {
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
flex-shrink: 0;
|
|
|
- margin-top: 24px; /* 调整这个值来对齐第二行 */
|
|
|
+ margin-top: 25px;
|
|
|
}
|
|
|
|
|
|
.detail-key {
|
|
|
@@ -465,4 +612,197 @@ async function confirmExport() {
|
|
|
margin-top: 8px;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+.suggestions {
|
|
|
+ margin-top: 8px;
|
|
|
+ max-height: 200px;
|
|
|
+ overflow-y: auto;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.suggestion-item {
|
|
|
+ padding: 8px 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+}
|
|
|
+
|
|
|
+.suggestion-item:hover,
|
|
|
+.suggestion-item-active {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ color: #409eff;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.selected-words-container {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 10px;
|
|
|
+ min-height: 200px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.selected-word-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ padding: 4px 8px;
|
|
|
+ background-color: #f0f9eb;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e1f3d8;
|
|
|
+}
|
|
|
+
|
|
|
+.selected-word-item span {
|
|
|
+ color: #67c23a;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.image-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.preview-image {
|
|
|
+ max-width: 100%;
|
|
|
+ max-height: 100%;
|
|
|
+ object-fit: contain;
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: stretch;
|
|
|
+ gap: 20px;
|
|
|
+ height: 500px;
|
|
|
+ padding: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.left-section {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20px;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.input-section {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.input-wrapper {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.word-input {
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.suggestions {
|
|
|
+ position: absolute;
|
|
|
+ top: 100%;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ margin-top: 4px;
|
|
|
+ max-height: 300px;
|
|
|
+ overflow-y: auto;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ background-color: white;
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
+ z-index: 1000;
|
|
|
+}
|
|
|
+
|
|
|
+.suggestion-item {
|
|
|
+ padding: 8px 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+}
|
|
|
+
|
|
|
+.suggestion-item:hover,
|
|
|
+.suggestion-item-active {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ color: #409eff;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.suggestion-item-active {
|
|
|
+ background-color: #ecf5ff;
|
|
|
+}
|
|
|
+
|
|
|
+.selected-words-container {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 10px;
|
|
|
+ min-height: 300px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ background-color: white;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.selected-word-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ padding: 6px 12px;
|
|
|
+ background-color: #f0f9eb;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e1f3d8;
|
|
|
+ transition: all 0.3s;
|
|
|
+}
|
|
|
+
|
|
|
+.selected-word-item:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.selected-word-item span {
|
|
|
+ color: #67c23a;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.divider {
|
|
|
+ margin: 0;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.right-section {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.image-container {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ background-color: white;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.preview-image {
|
|
|
+ max-width: 100%;
|
|
|
+ max-height: 100%;
|
|
|
+ object-fit: contain;
|
|
|
+}
|
|
|
</style>
|