feat: Add Export in some menu

fix: Left menu height
fix: Export csv when double quotation marks in data
This commit is contained in:
lejianwen
2025-06-15 17:16:27 +08:00
parent 42cc3974d4
commit fc7e0595a1
12 changed files with 84 additions and 4286 deletions
+4 -1
View File
@@ -13,5 +13,8 @@
}) })
</script> </script>
<style scoped> <style scoped lang="scss">
.scroll-sidebar {
background-color: #2d3a4b;
}
</style> </style>
-1
View File
@@ -60,7 +60,6 @@
} }
.app-left { .app-left {
height: 100%;
transition: width 0.5s; transition: width 0.5s;
} }
+1 -1
View File
@@ -38,7 +38,7 @@ export function jsonToCsv (data) {
let keys = Object.keys(data[0]) let keys = Object.keys(data[0])
csv += keys.join(',') + '\n' csv += keys.join(',') + '\n'
data.forEach(row => { data.forEach(row => {
csv += keys.map(key => `"${row[key]}"`).join(',') + '\n' csv += keys.map(key => `"${row[key].toString().replaceAll('"', '""')}"`).join(',') + '\n'
}) })
return new Blob([csv], { type: 'text/csv' }) return new Blob([csv], { type: 'text/csv' })
} }
+1 -1
View File
@@ -447,7 +447,7 @@
"One": "新密码" "One": "新密码"
}, },
"NewPasswordEqualOldPassword": { "NewPasswordEqualOldPassword": {
"One": "New Password cannot be the same as Old Password" "One": "新密码不能与旧密码相同"
}, },
"ConfirmPassword": { "ConfirmPassword": {
"One": "确认密码" "One": "确认密码"
-4272
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -11,6 +11,7 @@
<el-form-item> <el-form-item>
<el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button> <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
<el-button type="danger" @click="toBatchDelete">{{ T('BatchDelete') }}</el-button> <el-button type="danger" @click="toBatchDelete">{{ T('BatchDelete') }}</el-button>
<el-button type="success" @click="toExport">{{ T('Export') }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@@ -62,6 +63,7 @@
handlerQuery, handlerQuery,
del, del,
batchdel, batchdel,
toExport,
} = useRepositories() } = useRepositories()
onMounted(getList) onMounted(getList)
+2
View File
@@ -11,6 +11,7 @@
<el-form-item> <el-form-item>
<el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button> <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
<el-button type="danger" @click="toBatchDelete">{{ T('BatchDelete') }}</el-button> <el-button type="danger" @click="toBatchDelete">{{ T('BatchDelete') }}</el-button>
<el-button type="success" @click="toExport">{{ T('Export') }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@@ -109,6 +110,7 @@
handlerQuery, handlerQuery,
del, del,
batchdel, batchdel,
toExport,
} = useFileRepositories() } = useFileRepositories()
onMounted(getList) onMounted(getList)
+26 -1
View File
@@ -1,9 +1,9 @@
import { reactive } from 'vue' import { reactive } from 'vue'
import { list, remove, fileList, fileRemove, batchDelete, fileBatchDelete } from '@/api/audit' import { list, remove, fileList, fileRemove, batchDelete, fileBatchDelete } from '@/api/audit'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { useRoute } from 'vue-router'
import { formatTime } from '@/utils/time' import { formatTime } from '@/utils/time'
import { T } from '@/utils/i18n' import { T } from '@/utils/i18n'
import { downBlob, jsonToCsv } from '@/utils/file'
export function useRepositories () { export function useRepositories () {
const listRes = reactive({ const listRes = reactive({
@@ -73,6 +73,17 @@ export function useRepositories () {
getList() getList()
} }
} }
const toExport = async () => {
const q = { ...listQuery }
q.page_size = 1000000
q.page = 1
const res = await list(q).catch(_ => false)
if (res) {
const csv = jsonToCsv(res.data.list)
downBlob(csv, 'connectLog.csv')
}
}
return { return {
listRes, listRes,
listQuery, listQuery,
@@ -80,6 +91,7 @@ export function useRepositories () {
handlerQuery, handlerQuery,
del, del,
batchdel, batchdel,
toExport,
} }
} }
@@ -151,6 +163,18 @@ export function useFileRepositories () {
getList() getList()
} }
} }
const toExport = async () => {
const q = { ...listQuery }
q.page_size = 1000000
q.page = 1
const res = await fileList(q).catch(_ => false)
if (res) {
const csv = jsonToCsv(res.data.list)
downBlob(csv, 'fileTransformLog.csv')
}
}
return { return {
listRes, listRes,
listQuery, listQuery,
@@ -158,5 +182,6 @@ export function useFileRepositories () {
handlerQuery, handlerQuery,
del, del,
batchdel, batchdel,
toExport,
} }
} }
+17 -1
View File
@@ -2,10 +2,10 @@ import { reactive, ref } from 'vue'
import { list as admin_fetchPeers } from '@/api/peer' import { list as admin_fetchPeers } from '@/api/peer'
import { list as my_fetchPeers } from '@/api/my/peer' import { list as my_fetchPeers } from '@/api/my/peer'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { useRoute } from 'vue-router'
import { T } from '@/utils/i18n' import { T } from '@/utils/i18n'
import { batchDelete as admin_batchDelete, list as admin_list, remove as admin_remove } from '@/api/login_log' import { batchDelete as admin_batchDelete, list as admin_list, remove as admin_remove } from '@/api/login_log'
import { batchDelete as my_batchDelete, list as my_list, remove as my_remove } from '@/api/my/login_log' import { batchDelete as my_batchDelete, list as my_list, remove as my_remove } from '@/api/my/login_log'
import { downBlob, jsonToCsv } from '@/utils/file'
const apis = { const apis = {
admin: { batchDelete: admin_batchDelete, list: admin_list, remove: admin_remove, fetchPeers: admin_fetchPeers }, admin: { batchDelete: admin_batchDelete, list: admin_list, remove: admin_remove, fetchPeers: admin_fetchPeers },
@@ -95,6 +95,21 @@ export function useRepositories (api_type = 'my') {
} }
} }
// only Admin
const toExport = async () => {
if (api_type !== 'admin') {
return false
}
const q = { ...listQuery }
q.page_size = 1000000
q.page = 1
const res = await admin_list(q).catch(_ => false)
if (res) {
const csv = jsonToCsv(res.data.list)
downBlob(csv, 'loginLog.csv')
}
}
return { return {
listRes, listRes,
listQuery, listQuery,
@@ -102,5 +117,6 @@ export function useRepositories (api_type = 'my') {
handlerQuery, handlerQuery,
del, del,
batchdel, batchdel,
toExport,
} }
} }
+5
View File
@@ -15,6 +15,7 @@
<el-form-item> <el-form-item>
<el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button> <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
<el-button type="danger" @click="toBatchDelete">{{ T('BatchDelete') }}</el-button> <el-button type="danger" @click="toBatchDelete">{{ T('BatchDelete') }}</el-button>
<el-button type="success" @click="toExport">{{ T('Export') }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@@ -62,6 +63,8 @@
import { loadAllUsers } from '@/global' import { loadAllUsers } from '@/global'
import { useRepositories } from '@/views/login/log.js' import { useRepositories } from '@/views/login/log.js'
import { T } from '@/utils/i18n' import { T } from '@/utils/i18n'
import { list } from '@/api/peer'
import { downBlob, jsonToCsv } from '@/utils/file'
const { allUsers, getAllUsers } = loadAllUsers() const { allUsers, getAllUsers } = loadAllUsers()
getAllUsers() getAllUsers()
@@ -73,6 +76,7 @@
handlerQuery, handlerQuery,
del, del,
batchdel, batchdel,
toExport,
} = useRepositories('admin') } = useRepositories('admin')
onMounted(getList) onMounted(getList)
@@ -91,6 +95,7 @@
} }
batchdel(multipleSelection.value) batchdel(multipleSelection.value)
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
+13 -6
View File
@@ -1,9 +1,10 @@
import { onMounted, reactive, watch } from 'vue' import { reactive } from 'vue'
import { list, remove, changePwd } from '@/api/user' import { list, remove, changePwd } from '@/api/user'
import { list as groups } from '@/api/group' import { list as groups } from '@/api/group'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ElMessageBox, ElMessage } from 'element-plus' import { ElMessageBox, ElMessage } from 'element-plus'
import { T } from '@/utils/i18n' import { T } from '@/utils/i18n'
import { downBlob, jsonToCsv } from '@/utils/file'
export function useRepositories () { export function useRepositories () {
@@ -42,18 +43,24 @@ export function useRepositories () {
listRes.groups = res.data.list listRes.groups = res.data.list
} }
} }
onMounted(getGroups)
onMounted(getList) const toExport = async () => {
const q = { ...listQuery }
watch(() => listQuery.page, getList) q.page_size = 1000000
watch(() => listQuery.page_size, handlerQuery) q.page = 1
const res = await list(q).catch(_ => false)
if (res) {
const csv = jsonToCsv(res.data.list)
downBlob(csv, 'users.csv')
}
}
return { return {
listRes, listRes,
listQuery, listQuery,
handlerQuery, handlerQuery,
getList, getList,
getGroups, getGroups,
toExport,
} }
} }
+11
View File
@@ -8,6 +8,7 @@
<el-form-item> <el-form-item>
<el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button> <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
<el-button type="danger" @click="toAdd">{{ T('Add') }}</el-button> <el-button type="danger" @click="toAdd">{{ T('Add') }}</el-button>
<el-button type="success" @click="toExport">{{ T('Export') }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@@ -63,14 +64,24 @@
import { DISABLE_STATUS, ENABLE_STATUS } from '@/utils/common_options' import { DISABLE_STATUS, ENABLE_STATUS } from '@/utils/common_options'
import { update } from '@/api/user' import { update } from '@/api/user'
import { ElMessageBox, ElMessage } from 'element-plus' import { ElMessageBox, ElMessage } from 'element-plus'
import { onMounted, watch } from 'vue'
//列表 //列表
const { const {
listRes, listRes,
listQuery, listQuery,
handlerQuery, handlerQuery,
getList, getList,
getGroups,
toExport,
} = useRepositories() } = useRepositories()
onMounted(getGroups)
onMounted(getList)
watch(() => listQuery.page, getList)
watch(() => listQuery.page_size, handlerQuery)
const { toEdit, toAdd, toAddressBook, toTag } = useToEditOrAdd() const { toEdit, toAdd, toAddressBook, toTag } = useToEditOrAdd()
const { changePass } = useChangePwd() const { changePass } = useChangePwd()