init
This commit is contained in:
122
src/views/AccessRecords.vue
Normal file
122
src/views/AccessRecords.vue
Normal file
@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div id="app-container">
|
||||
<el-button size="small" @click="back" type="primary" plain icon="back"
|
||||
>返回</el-button
|
||||
>
|
||||
<el-form
|
||||
class="query-form"
|
||||
inline
|
||||
size="small"
|
||||
:model="queryParams"
|
||||
ref="queryParamsRef"
|
||||
>
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="queryParams.startTime"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择开始时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="queryParams.endTime"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择结束时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="search" @click="handleSearch"
|
||||
>搜索
|
||||
</el-button>
|
||||
<el-button type="primary" icon="refresh" @click="resetQuery"
|
||||
>重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="logList" v-loading>
|
||||
<el-table-column label="访问时间" prop="visitTime"></el-table-column>
|
||||
<el-table-column label="访问IP" prop="visitIp"></el-table-column>
|
||||
<el-table-column label="访问地区" prop="visitAddress"></el-table-column>
|
||||
<el-table-column label="访问设备" prop="visitDevice"></el-table-column>
|
||||
<el-table-column label="访问后缀" prop="urlSuffix"></el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
background
|
||||
:page-size="queryParams.pageSize"
|
||||
:current-page="queryParams.pageNum"
|
||||
@update:current-page="handleCurrentChange"
|
||||
layout="prev, pager, next"
|
||||
:total="total"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AccessRecords" lang="ts">
|
||||
import { reactive, ref, toRefs } from "vue";
|
||||
import { listLogList } from "@/api/shortlink";
|
||||
import type { LogListParams } from "@/models/LogListParams";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const logList = ref([]);
|
||||
const total = ref(0);
|
||||
const data = reactive<{ queryParams: LogListParams }>({
|
||||
queryParams: {
|
||||
urlSuffix: route.params.suffix as string,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
});
|
||||
|
||||
const { queryParams } = toRefs(data);
|
||||
const getList = async () => {
|
||||
const resp = await listLogList(queryParams.value);
|
||||
logList.value = resp.data.rows;
|
||||
total.value = resp.data.total;
|
||||
};
|
||||
const queryParamsRef = ref();
|
||||
const resetQuery = () => {
|
||||
queryParams.value = {
|
||||
urlSuffix: route.params.suffix as string,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
if (queryParamsRef.value) {
|
||||
queryParamsRef.value.resetFields();
|
||||
}
|
||||
getList();
|
||||
};
|
||||
const handleSearch = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
const handleCurrentChange = (page: number) => {
|
||||
queryParams.value.pageNum = page;
|
||||
getList();
|
||||
};
|
||||
const back = () => {
|
||||
router.push("/");
|
||||
};
|
||||
getList();
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
#app-container {
|
||||
padding: 20px;
|
||||
|
||||
.query-form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
</style>
|
383
src/views/GenShortLink.vue
Normal file
383
src/views/GenShortLink.vue
Normal file
@ -0,0 +1,383 @@
|
||||
<template>
|
||||
<div id="app-container" class="p-3">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button @click="handleAdd" type="primary" size="small" icon="plus"
|
||||
>新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
icon="delete"
|
||||
@click="handleDelete()"
|
||||
type="danger"
|
||||
size="small"
|
||||
:disabled="multiple"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-table
|
||||
class="short-link-table"
|
||||
:data="shortLinkList"
|
||||
@selection-change="handleSelectionChange"
|
||||
v-loading
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="50" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="名称"
|
||||
prop="name"
|
||||
width="120"
|
||||
></el-table-column>
|
||||
<el-table-column align="center" label="短链接网址" prop="jumpUrl">
|
||||
<template #default="{ row }">
|
||||
<el-row :gutter="5">
|
||||
<el-col :span="18">
|
||||
<div>
|
||||
<a
|
||||
class="short-link"
|
||||
:href="`//${row.urlPrefix}/dlj/${row.urlSuffix}`"
|
||||
>
|
||||
{{ row.urlPrefix }}/dlj/{{ row.urlSuffix }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="jump-url">{{ row.jumpUrl }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-row :gutter="5">
|
||||
<el-col :span="1.5">
|
||||
<i
|
||||
class="iconfont icon-qrcode"
|
||||
:style="{
|
||||
cursor: 'pointer',
|
||||
}"
|
||||
@click="handleShowQrCode(row)"
|
||||
></i>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-icon
|
||||
:style="{
|
||||
cursor: 'pointer',
|
||||
}"
|
||||
@click="copyToClipboard(row.jumpUrl)"
|
||||
>
|
||||
<CopyDocument />
|
||||
</el-icon>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="访问次数" width="160" align="center">
|
||||
<template #default="{ row }">
|
||||
<div class="visit-count">
|
||||
<div>
|
||||
<span class="title">今日</span>
|
||||
<span class="count">{{ row.todayVisit }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="title">累计</span>
|
||||
<span class="count">{{ row.historyVisit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="过期时间"
|
||||
prop="validityTime"
|
||||
></el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
@click="handleUpdate(row.id)"
|
||||
link
|
||||
size="small"
|
||||
type="warning"
|
||||
>编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="handleDelete(row.id)"
|
||||
link
|
||||
size="small"
|
||||
type="danger"
|
||||
>删除
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="goLogList(row.urlSuffix)"
|
||||
link
|
||||
size="small"
|
||||
type="primary"
|
||||
>访问记录
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:page-size="queryParams.pageSize"
|
||||
:current-page="queryParams.pageNum"
|
||||
@update:current-page="handleCurrentChange"
|
||||
:total="total"
|
||||
/>
|
||||
</div>
|
||||
<el-dialog v-model="showEditDialog" title="新增短链接">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" :label-width="100">
|
||||
<el-form-item label="链接标题" prop="name">
|
||||
<el-input placeholder="请输入链接标题" v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="跳转链接" prop="jumpUrl">
|
||||
<el-input
|
||||
placeholder="请输入跳转链接"
|
||||
v-model="form.jumpUrl"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="URL前缀" prop="urlPrefix">
|
||||
<el-input
|
||||
placeholder="请输入URL前缀"
|
||||
v-model="form.urlPrefix"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="有效期" prop="validityTime">
|
||||
<el-date-picker
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
v-model="form.validityTime"
|
||||
type="datetime"
|
||||
placeholder="请选择过期时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="showEditDialog = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="showQrCodeDialog" width="320">
|
||||
<qrcode-vue
|
||||
class="qrcode-canvas"
|
||||
:value="qrcodeValue"
|
||||
:size="280"
|
||||
ref="qrcodeRef"
|
||||
:margin="1"
|
||||
></qrcode-vue>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="downloadQrCode"
|
||||
:style="{
|
||||
width: '100%',
|
||||
marginTop: '10px',
|
||||
}"
|
||||
>下载二维码
|
||||
</el-button>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="GenShortLink">
|
||||
import { addShortLink, deleteShortLink, getDetailById, listShortLink, updateShortLink } from "@/api/shortlink";
|
||||
import { reactive, ref, toRefs } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import type { ShortLink } from "@/models/shortLink";
|
||||
|
||||
import QrcodeVue from "qrcode.vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const qrcodeRef = ref();
|
||||
const shortLinkList = ref<ShortLink[]>([]);
|
||||
const showEditDialog = ref(false);
|
||||
const editDialogTitle = ref<string>("");
|
||||
const showQrCodeDialog = ref(false);
|
||||
const qrcodeValue = ref("");
|
||||
const qrcodeFileName = ref("");
|
||||
const formRef = ref();
|
||||
const data = reactive<{
|
||||
queryParams: { pageNum: number; pageSize: number };
|
||||
form: ShortLink;
|
||||
rules: any;
|
||||
}>({
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
form: {},
|
||||
rules: {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入短链接名称",
|
||||
trigger: "blur"
|
||||
}
|
||||
],
|
||||
jumpUrl: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入短链接",
|
||||
trigger: "blur"
|
||||
}
|
||||
],
|
||||
urlPrefix: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入URL前缀",
|
||||
trigger: "blur"
|
||||
}
|
||||
],
|
||||
validityTime: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择过期时间",
|
||||
trigger: "change"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
const { form, queryParams, rules } = toRefs(data);
|
||||
const total = ref<number>(0);
|
||||
const router = useRouter();
|
||||
const getList = async () => {
|
||||
const resp = await listShortLink(queryParams.value);
|
||||
shortLinkList.value = resp.data.rows;
|
||||
total.value = resp.data.total;
|
||||
};
|
||||
const handleShowQrCode = (data: ShortLink) => {
|
||||
qrcodeValue.value = `${data.urlPrefix}/dlj/${data.urlSuffix}`;
|
||||
qrcodeFileName.value = `${data.urlSuffix}.png`;
|
||||
showQrCodeDialog.value = true;
|
||||
};
|
||||
const goLogList = (suffix: string) => {
|
||||
router.push({
|
||||
path: `/log-list/${suffix}`
|
||||
});
|
||||
};
|
||||
const reset = () => {
|
||||
form.value = {};
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
};
|
||||
// 多选框选中数据
|
||||
const linkIds = ref<number[]>([]);
|
||||
const multiple = ref(true);
|
||||
const single = ref(true);
|
||||
const downloadQrCode = () => {
|
||||
console.log(qrcodeRef.value);
|
||||
const qrcodeCanvas: HTMLCanvasElement | null =
|
||||
document.querySelector(".qrcode-canvas");
|
||||
qrcodeCanvas?.toBlob((blob: Blob | null) => {
|
||||
if (blob != null) {
|
||||
let aLink = document.createElement("a");
|
||||
aLink.download = qrcodeFileName.value;
|
||||
aLink.href = URL.createObjectURL(blob);
|
||||
aLink.click();
|
||||
} else {
|
||||
ElMessage.error("下载二维码失败");
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleSelectionChange = (selection: ShortLink[]) => {
|
||||
linkIds.value = selection.map((item: ShortLink) => item.id!);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
const copyToClipboard = (data: string) => {
|
||||
navigator.clipboard.writeText(data);
|
||||
ElMessage.success("短链接已拷贝到剪贴板");
|
||||
};
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
editDialogTitle.value = "新增短链接";
|
||||
showEditDialog.value = true;
|
||||
};
|
||||
const handleUpdate = async (id: number) => {
|
||||
reset();
|
||||
const { data } = await getDetailById(id);
|
||||
form.value = data.data;
|
||||
editDialogTitle.value = "修改短链接";
|
||||
showEditDialog.value = true;
|
||||
};
|
||||
const handleDelete = (id?: string) => {
|
||||
let ids: string;
|
||||
if (id) {
|
||||
ids = id;
|
||||
} else {
|
||||
ids = linkIds.value.join(",");
|
||||
}
|
||||
ElMessageBox.confirm("确认删除该短链接吗", "删除短链接", {
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
await deleteShortLink(ids);
|
||||
ElMessage.success("删除短链接成功");
|
||||
getList();
|
||||
});
|
||||
};
|
||||
|
||||
const submitForm = async () => {
|
||||
if (form.value.id) {
|
||||
await updateShortLink(form.value);
|
||||
ElMessage.success("短链接修改成功");
|
||||
showEditDialog.value = false;
|
||||
getList();
|
||||
} else {
|
||||
await addShortLink(form.value);
|
||||
ElMessage.success("短链接添加成功");
|
||||
showEditDialog.value = false;
|
||||
getList();
|
||||
}
|
||||
};
|
||||
const handleCurrentChange = (page: number) => {
|
||||
queryParams.value.pageNum = page;
|
||||
getList();
|
||||
};
|
||||
getList();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#app-container {
|
||||
padding: 20px;
|
||||
|
||||
a.short-link {
|
||||
//overflow: hidden;
|
||||
//text-overflow: ellipsis;
|
||||
//white-space: nowrap;
|
||||
}
|
||||
|
||||
.jump-url {
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
//width: 80%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.visit-count {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 20px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.short-link-table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user