feat: 添加编码管理模版

This commit is contained in:
皓月归尘 2025-02-14 14:36:54 +08:00
parent 8643b57899
commit f5424660a0
6 changed files with 525 additions and 1 deletions

View File

@ -103,6 +103,9 @@ menus:FourZeroFour: "404"
menus:FourZeroOne: "403"
menus:Five: "500"
menus:SystemConfig: System Config
menus:CodeManager: Code Manager
menus:CodeQueryIndex: Code Query
menus:CodeQueryLog: Query Log
status:Load: Loading...
status:Message: Message
status:Notify: Notify

View File

@ -103,6 +103,9 @@ menus:FourZeroFour: "404"
menus:FourZeroOne: "403"
menus:Five: "500"
menus:SystemConfig: 系统配置
menus:CodeManager: 编码管理
menus:CodeQueryIndex: 编码查询
menus:CodeQueryLog: 查询日志
status:Load: 加载中...
status:Message: 消息
status:Notify: 通知

View File

@ -77,7 +77,8 @@
"vue-json-pretty": "^2.4.0",
"vue-router": "^4.5.0",
"vue-tippy": "^6.5.0",
"vue-types": "^5.1.3"
"vue-types": "^5.1.3",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^19.6.0",

72
pnpm-lock.yaml generated
View File

@ -98,6 +98,9 @@ importers:
vue-types:
specifier: ^5.1.3
version: 5.1.3(vue@3.5.13(typescript@5.6.3))
xlsx:
specifier: ^0.18.5
version: 0.18.5
devDependencies:
'@commitlint/cli':
specifier: ^19.6.0
@ -1298,6 +1301,10 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
adler-32@1.3.1:
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==, tarball: https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz}
engines: {node: '>=0.8'}
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
@ -1446,6 +1453,10 @@ packages:
caniuse-lite@1.0.30001687:
resolution: {integrity: sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==}
cfb@1.2.2:
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==, tarball: https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz}
engines: {node: '>=0.8'}
chalk@4.1.1:
resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==}
engines: {node: '>=10'}
@ -1498,6 +1509,10 @@ packages:
code-inspector-plugin@0.18.2:
resolution: {integrity: sha512-LKOhA4YsoUZ6Dq4OQKP7G+kPcfeYGLoIQz7EDG4yoL5mqSu+uWR+0QvzoDc4HGXQ0jpkzEwlatbH6fBlbPiwKQ==}
codepage@1.15.0:
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==, tarball: https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz}
engines: {node: '>=0.8'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@ -1576,6 +1591,11 @@ packages:
typescript:
optional: true
crc-32@1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==, tarball: https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz}
engines: {node: '>=0.8'}
hasBin: true
cropperjs@1.6.2:
resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==, tarball: https://registry.npmmirror.com/cropperjs/-/cropperjs-1.6.2.tgz}
@ -1990,6 +2010,10 @@ packages:
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
engines: {node: '>= 6'}
frac@1.1.2:
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==, tarball: https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz}
engines: {node: '>=0.8'}
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@ -3200,6 +3224,10 @@ packages:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
ssf@0.11.2:
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==, tarball: https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz}
engines: {node: '>=0.8'}
std-env@3.8.0:
resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==}
@ -3633,10 +3661,18 @@ packages:
resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==}
engines: {node: '>=18'}
wmf@1.0.2:
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==, tarball: https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz}
engines: {node: '>=0.8'}
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
word@0.3.0:
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==, tarball: https://registry.npmmirror.com/word/-/word-0.3.0.tgz}
engines: {node: '>=0.8'}
wrap-ansi@6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==, tarball: https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz}
engines: {node: '>=8'}
@ -3657,6 +3693,11 @@ packages:
resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
xlsx@0.18.5:
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==, tarball: https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz}
engines: {node: '>=0.8'}
hasBin: true
xml-name-validator@4.0.0:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
engines: {node: '>=12'}
@ -4825,6 +4866,8 @@ snapshots:
acorn@8.14.0: {}
adler-32@1.3.1: {}
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
@ -4988,6 +5031,11 @@ snapshots:
caniuse-lite@1.0.30001687: {}
cfb@1.2.2:
dependencies:
adler-32: 1.3.1
crc-32: 1.2.2
chalk@4.1.1:
dependencies:
ansi-styles: 4.3.0
@ -5068,6 +5116,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
codepage@1.15.0: {}
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@ -5136,6 +5186,8 @@ snapshots:
optionalDependencies:
typescript: 5.6.3
crc-32@1.2.2: {}
cropperjs@1.6.2: {}
cross-spawn@7.0.6:
@ -5613,6 +5665,8 @@ snapshots:
combined-stream: 1.0.8
mime-types: 2.1.35
frac@1.1.2: {}
fraction.js@4.3.7: {}
framesync@6.1.2:
@ -6744,6 +6798,10 @@ snapshots:
split2@4.2.0: {}
ssf@0.11.2:
dependencies:
frac: 1.1.2
std-env@3.8.0:
optional: true
@ -7257,8 +7315,12 @@ snapshots:
dependencies:
string-width: 7.2.0
wmf@1.0.2: {}
word-wrap@1.2.5: {}
word@0.3.0: {}
wrap-ansi@6.2.0:
dependencies:
ansi-styles: 4.3.0
@ -7288,6 +7350,16 @@ snapshots:
imurmurhash: 0.1.4
signal-exit: 4.1.0
xlsx@0.18.5:
dependencies:
adler-32: 1.3.1
cfb: 1.2.2
codepage: 1.15.0
crc-32: 1.2.2
ssf: 0.11.2
wmf: 1.0.2
word: 0.3.0
xml-name-validator@4.0.0: {}
y18n@4.0.3: {}

195
src/views/codes/index.vue Normal file
View File

@ -0,0 +1,195 @@
<template>
<div class="p-4">
<!-- 查询区域 -->
<el-card class="mb-4">
<div class="flex items-center space-x-4">
<!-- 富文本输入框 -->
<el-input
v-model="queryText"
type="textarea"
autosize
:rows="2"
placeholder="请输入查询文本"
class="flex-1"
/>
<!-- 查询按钮 -->
<el-button type="primary" @click="handleQuery">查询</el-button>
<!-- 清空查询按钮 -->
<el-button @click="clearQuery">清空查询</el-button>
<!-- 上传文件按钮 -->
<el-button @click="toggleUploadArea">
{{ showUploadArea ? "隐藏上传区域" : "上传文件" }}
</el-button>
</div>
</el-card>
<!-- 上传文件区域 -->
<el-card v-if="showUploadArea" class="mb-4">
<div class="flex items-center space-x-4">
<!-- 拖拽上传组件 -->
<el-upload
class="upload-demo"
drag
action="https://jsonplaceholder.typicode.com/posts/"
:on-success="handleUploadSuccess"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
<!-- 下载模板按钮 -->
<el-button type="primary" @click="downloadTemplate"
>下载上传文件模板</el-button
>
</div>
</el-card>
<!-- 查询结果区域 -->
<el-card>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="queryText" label="查询文本" />
<el-table-column prop="result1Code" label="查询结果1编码" />
<el-table-column prop="result1" label="查询结果1" />
<el-table-column prop="result2Code" label="查询结果2编码" />
<el-table-column prop="result2" label="查询结果2" />
<el-table-column label="操作">
<template #default="scope">
<el-button type="text" @click="showDetail(scope.row)"
>详情</el-button
>
<el-button type="text" @click="showFeedback(scope.row)"
>反馈</el-button
>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 详情抽屉 -->
<el-drawer v-model="detailVisible" title="详情" :with-header="false">
<div class="p-4">
<h3 class="text-lg font-bold mb-4">查询详情</h3>
<p><strong>查询文本:</strong> {{ currentRow.queryText }}</p>
<p><strong>查询结果1编码:</strong> {{ currentRow.result1Code }}</p>
<p><strong>查询结果1:</strong> {{ currentRow.result1 }}</p>
<p><strong>查询结果2编码:</strong> {{ currentRow.result2Code }}</p>
<p><strong>查询结果2:</strong> {{ currentRow.result2 }}</p>
</div>
</el-drawer>
<!-- 反馈弹窗 -->
<el-dialog v-model="feedbackVisible" title="反馈" width="30%">
<el-form :model="feedbackForm" label-width="120px">
<el-form-item label="查询结果是否正确">
<el-radio-group v-model="feedbackForm.isCorrect">
<el-radio :label="true">正确</el-radio>
<el-radio :label="false">错误</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="!feedbackForm.isCorrect" label="正确结果">
<el-input
v-model="feedbackForm.correctResult"
type="textarea"
:rows="2"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="feedbackVisible = false">取消</el-button>
<el-button type="primary" @click="submitFeedback">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { UploadFilled } from "@element-plus/icons-vue";
defineOptions({
name: "CodeQueryIndex"
});
//
const queryText = ref("");
//
const showUploadArea = ref(false);
//
const tableData = ref([
{
queryText: "示例查询文本1",
result1Code: "001",
result1: "结果1",
result2Code: "002",
result2: "结果2"
},
{
queryText: "示例查询文本2",
result1Code: "003",
result1: "结果3",
result2Code: "004",
result2: "结果4"
}
]);
//
const detailVisible = ref(false);
const currentRow = ref({});
//
const feedbackVisible = ref(false);
const feedbackForm = ref({
isCorrect: true,
correctResult: ""
});
//
const handleQuery = () => {
console.log("查询文本:", queryText.value);
//
};
//
const clearQuery = () => {
queryText.value = "";
};
//
const toggleUploadArea = () => {
showUploadArea.value = !showUploadArea.value;
};
//
const handleUploadSuccess = (response: any, file: any) => {
console.log("文件上传成功:", file);
};
//
const downloadTemplate = () => {
console.log("下载模板");
//
};
//
const showDetail = (row: any) => {
currentRow.value = row;
detailVisible.value = true;
};
//
const showFeedback = (row: any) => {
currentRow.value = row;
feedbackVisible.value = true;
};
//
const submitFeedback = () => {
console.log("提交反馈:", feedbackForm.value);
feedbackVisible.value = false;
};
</script>
<style scoped lang="scss">
.upload-demo {
flex: 1;
}
</style>

250
src/views/codes/log.vue Normal file
View File

@ -0,0 +1,250 @@
<template>
<div class="p-4">
<!-- 查询区域 -->
<el-card class="mb-4">
<div class="flex items-center space-x-4">
<!-- 查询文本 -->
<el-input
v-model="queryParams.queryText"
placeholder="请输入查询文本"
class="flex-1"
/>
<!-- 查询时间范围 -->
<el-date-picker
v-model="queryParams.timeRange"
type="datetimerange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
<!-- 查询按钮 -->
<el-button type="primary" @click="handleQuery">查询</el-button>
<!-- 清空表单按钮 -->
<el-button @click="clearQuery">清空表单</el-button>
</div>
</el-card>
<!-- 结果显示区域 -->
<el-card>
<div class="mb-4 flex items-center space-x-4">
<!-- 全部导出按钮 -->
<el-button type="primary" @click="exportAll">全部导出</el-button>
<!-- 多选导出按钮 -->
<el-button
type="primary"
:disabled="selectedRows.length === 0"
@click="exportSelected"
>
导出选中项
</el-button>
</div>
<el-table
:data="pagedTableData"
style="width: 100%"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="queryText" label="查询文本" />
<el-table-column prop="result1Code" label="查询结果编码1" />
<el-table-column prop="result1" label="查询结果1" />
<el-table-column prop="result2Code" label="查询结果编码2" />
<el-table-column prop="result2" label="查询结果2" />
<el-table-column prop="queryTime" label="查询时间" />
<el-table-column label="操作">
<template #default="scope">
<el-button type="text" @click="showDetail(scope.row)"
>详情</el-button
>
<el-button type="text" @click="showFeedback(scope.row)"
>反馈</el-button
>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination
class="mt-4"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
:page-size="pageSize"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</el-card>
<!-- 详情抽屉 -->
<el-drawer v-model="detailVisible" title="详情" :with-header="false">
<div class="p-4">
<h3 class="text-lg font-bold mb-4">查询详情</h3>
<p><strong>查询文本:</strong> {{ currentRow.queryText }}</p>
<p><strong>查询结果编码1:</strong> {{ currentRow.result1Code }}</p>
<p><strong>查询结果1:</strong> {{ currentRow.result1 }}</p>
<p><strong>查询结果编码2:</strong> {{ currentRow.result2Code }}</p>
<p><strong>查询结果2:</strong> {{ currentRow.result2 }}</p>
<p><strong>查询时间:</strong> {{ currentRow.queryTime }}</p>
</div>
</el-drawer>
<!-- 反馈弹窗 -->
<el-dialog v-model="feedbackVisible" title="反馈" width="30%">
<el-form :model="feedbackForm" label-width="120px">
<el-form-item label="查询结果是否正确">
<el-radio-group v-model="feedbackForm.isCorrect">
<el-radio :label="true">正确</el-radio>
<el-radio :label="false">错误</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="!feedbackForm.isCorrect" label="正确结果">
<el-input
v-model="feedbackForm.correctResult"
type="textarea"
:rows="2"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="feedbackVisible = false">取消</el-button>
<el-button type="primary" @click="submitFeedback">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from "vue";
import { ElMessage } from "element-plus";
import * as XLSX from "xlsx";
defineOptions({
name: "CodeQueryLog"
});
//
const queryParams = ref({
queryText: "",
timeRange: []
});
//
const mockData = () => {
const data = [];
for (let i = 1; i <= 100; i++) {
data.push({
queryText: `示例查询文本${i}`,
result1Code: `00${i}`,
result1: `结果${i}`,
result2Code: `01${i}`,
result2: `结果${i + 1}`,
queryTime: new Date().toLocaleString()
});
}
return data;
};
//
const tableData = ref(mockData());
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = computed(() => tableData.value.length);
//
const pagedTableData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
return tableData.value.slice(start, end);
});
//
const selectedRows = ref<any[]>([]);
//
const detailVisible = ref(false);
const currentRow = ref({});
//
const feedbackVisible = ref(false);
const feedbackForm = ref({
isCorrect: true,
correctResult: ""
});
//
const handleQuery = () => {
console.log("查询参数:", queryParams.value);
//
};
//
const clearQuery = () => {
queryParams.value = {
queryText: "",
timeRange: []
};
};
//
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection;
};
// Excel
const exportAll = () => {
const worksheet = XLSX.utils.json_to_sheet(tableData.value);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "查询日志");
XLSX.writeFile(workbook, "查询日志.xlsx");
ElMessage.success("导出成功");
};
// Excel
const exportSelected = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning("请选择要导出的数据");
return;
}
const worksheet = XLSX.utils.json_to_sheet(selectedRows.value);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "查询日志");
XLSX.writeFile(workbook, "选中查询日志.xlsx");
ElMessage.success("导出成功");
};
//
const handleSizeChange = (size: number) => {
pageSize.value = size;
currentPage.value = 1; //
};
//
const handlePageChange = (page: number) => {
currentPage.value = page;
};
//
const showDetail = (row: any) => {
currentRow.value = row;
detailVisible.value = true;
};
//
const showFeedback = (row: any) => {
currentRow.value = row;
feedbackVisible.value = true;
};
//
const submitFeedback = () => {
console.log("提交反馈:", feedbackForm.value);
feedbackVisible.value = false;
};
</script>
<style scoped lang="scss">
.upload-demo {
flex: 1;
}
</style>