feat: 添加预览组件

master
绝弹 2024-01-08 08:10:03 +08:00
parent ff389d988d
commit 6bc11b3c95
11 changed files with 8816 additions and 153 deletions

View File

@ -23,6 +23,7 @@
"@vueuse/core": "^9.13.0",
"axios": "^1.5.0",
"dayjs": "^1.11.9",
"dplayer": "^1.27.1",
"ejs": "^3.1.9",
"less": "^4.2.0",
"lodash-es": "^4.17.21",

View File

@ -34,6 +34,9 @@ devDependencies:
dayjs:
specifier: ^1.11.9
version: 1.11.9
dplayer:
specifier: ^1.27.1
version: 1.27.1
ejs:
specifier: ^3.1.9
version: 3.1.9
@ -1785,6 +1788,16 @@ packages:
engines: {node: '>= 0.4'}
dev: true
/axios@1.2.3:
resolution: {integrity: sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==}
dependencies:
follow-redirects: 1.15.2
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
dev: true
/axios@1.5.0:
resolution: {integrity: sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==}
dependencies:
@ -1807,6 +1820,10 @@ packages:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
/balloon-css@1.2.0:
resolution: {integrity: sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A==}
dev: true
/base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true
@ -2724,6 +2741,16 @@ packages:
engines: {node: '>=12'}
dev: true
/dplayer@1.27.1:
resolution: {integrity: sha512-2laBMXs5V1B9zPwJ7eAIw/OBo+Xjvy03i4GHTk3Cg+IWbrq8rKMFO0fFr6ClAYotYOCcFGOvaJDkOZcgKllsCA==}
dependencies:
axios: 1.2.3
balloon-css: 1.2.0
promise-polyfill: 8.3.0
transitivePeerDependencies:
- debug
dev: true
/duplexer@0.1.2:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
dev: true
@ -5475,6 +5502,10 @@ packages:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: true
/promise-polyfill@8.3.0:
resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==}
dev: true
/promise.allsettled@1.0.6:
resolution: {integrity: sha512-22wJUOD3zswWFqgwjNHa1965LvqTX87WPu/lreY2KSd7SVcERfuZ4GfUaOnJNnvtoIv2yXT/W00YIGMetXtFXg==}
engines: {node: '>= 0.4'}

View File

@ -0,0 +1,224 @@
<template>
<teleport to="body">
<div
v-if="show"
ref="viewRef"
class="absolute top-0 left-0 w-full h-full flex items-center justify-center bg-[rgba(0,0,0,.6)]"
>
<div
ref="headerRef"
class="absolute top-0 left-0 w-full flex items-center justify-between gap-4 bg-[rgba(0,0,0,.3)] h-14 px-6 text-white"
>
<div>
<button
@click="onBack"
class="select-none bg-transparent text-white h-8 px-2 rounded hover:bg-[rgba(255,255,255,.1)]"
>
<i class="icon-park-outline-back mr-1"></i>
返回
</button>
<span class="mx-2">|</span>
<span class="ml-2 text-base">
<slot name="name">
<i :class="typeIcon"></i>
<span class="ml-2">{{ name }}</span>
</slot>
</span>
</div>
<div class="flex items-center gap-2">
<button v-if="download" class="bg-transparent text-white h-8 px-3 rounded hover:bg-[rgba(255,255,255,.1)]">
<i class="icon-park-outline-download mr-1"></i>
<span>下载</span>
</button>
<button class="bg-transparent text-white text-xl w-8 h-8 rounded hover:bg-[rgba(255,255,255,.1)]">
<i class="icon-park-outline-more-one"></i>
</button>
</div>
</div>
<div>
<div v-if="type === ViewType.VIDEO" ref="videoRef" class="w-[1280px]"></div>
<div v-else-if="type === ViewType.TEXT" class="w-[1280px] h-[600px] bg-[rgba(0,0,0,.1)] text-white leading-8">
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto">
<div class="py-4 px-5">
{{ url }}
</div>
</a-scrollbar>
</div>
<div v-else-if="type === ViewType.IMAGE">
<img :src="url" :alt="name" />
</div>
<div v-else-if="type === ViewType.AUDIO">
<div class="audio-player flex items-center gap-4 bg-[rgba(255,255,255,.1)] text-white px-4 py-4">
<div
@click="playing = !playing"
class="hover:bg-[rgba(255,255,255,.1)] h-8 px-1.5 flex items-center justify-center rounded"
>
<i v-if="playing" class="text-xl icon-park-outline-pause-one"></i>
<i v-else class="text-xl icon-park-outline-play"></i>
</div>
<div>
{{ currentFormated }}
</div>
<div class="w-96">
<audio ref="audioRef" src="" class="hidden"></audio>
<a-slider class="block!"></a-slider>
</div>
<div>
{{ durationFormated }}
</div>
<div class="dd">
<a-popover>
<div
@click="onMuteToggle"
class="text-xl hover:bg-[rgba(255,255,255,.1)] h-8 px-1.5 flex items-center justify-center rounded"
>
<i :class="volumeIcon"></i>
</div>
<template #content>
<div class="flex flex-col items-center">
<div class="w-6 text-center">
{{ volume }}
</div>
<a-slider class="min-w-auto!" v-model="volume" direction="vertical"></a-slider>
</div>
</template>
</a-popover>
</div>
</div>
</div>
<div v-else>
<div class="w-96 text-center bg-[rgba(0,0,0,.1)] text-white px-4 py-8">
<div class="">暂不支持该文件类型的预览</div>
<div v-if="download" class="mt-4">
<button class="text-white h-8 px-3 rounded bg-[rgba(255,255,255,.1)] hover:bg-[rgba(255,255,255,.2)]">
<i class="icon-park-outline-download mr-1"></i>
<span>下载文件</span>
</button>
</div>
</div>
</div>
</div>
<div class="absolute bottom-20"></div>
</div>
</teleport>
</template>
<script setup lang="tsx">
import { getIcon } from '@/pages/content/material/components/util';
import { useVModel } from '@vueuse/core';
import DPlayer from 'dplayer';
import numeral from 'numeral';
import { PropType } from 'vue';
enum ViewType {
TEXT = 'text',
IMAGE = 'image',
AUDIO = 'audio',
VIDEO = 'video',
}
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
type: {
type: String as PropType<'text' | 'image' | 'audio' | 'video'>,
},
url: {
type: String,
},
download: {
type: String,
},
name: {
type: String,
default: '失落的海峡.mp4',
},
});
let dplayer: any;
const emit = defineEmits(['update:visible']);
const show = useVModel(props, 'visible', emit);
const headerRef = ref<HTMLElement | null>(null);
const viewRef = ref<HTMLElement | null>(null);
const videoRef = ref<HTMLElement | null>(null);
const onBack = () => (show.value = false);
const playing = ref(true);
const volume = ref(50);
const volumeLast = ref(50);
const current = ref(18);
const duration = ref(120);
const currentFormated = computed(() => numeral(current.value).format('0:00'));
const durationFormated = computed(() => numeral(duration.value).format('0:00'));
const volumeIcon = computed(() => {
if (volume.value <= 0) {
return 'icon-park-outline-volume-mute';
} else if (volume.value <= 50) {
return 'icon-park-outline-volume-small';
} else {
return 'icon-park-outline-volume-notice';
}
});
const typeIcon = computed(() => {
return getIcon(props.type ?? 'text');
});
const onMuteToggle = () => {
if (volume.value === 0) {
volume.value = volumeLast.value;
} else {
volumeLast.value = volume.value;
volume.value = 0;
}
};
const initClickOutside = async () => {
await nextTick();
viewRef.value?.addEventListener('click', e => {
const target = e.target as HTMLElement;
if (target?.contains(headerRef.value)) {
return;
}
show.value = false;
});
};
const loadVideo = async () => {
if (dplayer) {
dplayer.destroy();
}
await nextTick();
if (!videoRef.value) {
return;
}
dplayer = new DPlayer({
container: videoRef.value,
video: {
url: props.url,
},
context: [],
});
};
watch(
() => props.visible,
val => {
if (!val) {
dplayer?.destroy();
return;
}
if (props.type === ViewType.VIDEO) {
loadVideo();
}
},
{
immediate: true,
}
);
</script>
<style scoped></style>

View File

@ -2,7 +2,7 @@
<BreadPage>
<template #content>
<a-tabs class="tabs-page">
<a-tab-pane key="1" title="素材管理">
<a-tab-pane key="1" title="全部素材">
<div class="overflow-hidden grid grid-cols-[auto_1fr] gap-2 m-4 mt-1">
<!-- <AnGroup class="bg-white p-4 w-[242px]" :current="current" @change="onCategoryChange"></AnGroup> -->
<div class="bg-white p-4">

View File

@ -1,117 +1,135 @@
<template>
<div class="m-4 bg-white p-4">
<user-table></user-table>
<a-button @click="visible = true">预览</a-button>
<an-viewer v-model:visible="visible" type="audio" :url="textUrl" :download="'s'" :name="'明日之星.png'"></an-viewer>
</div>
</template>
<script setup lang="tsx">
import { api } from '@/api';
import { useTable } from '@/components/AnTable';
import AnViewer from '@/components/AnViewer/AnViewer.vue';
const { component: UserTable } = useTable({
columns: [
{
dataIndex: 'id',
title: 'ID',
configable: false,
},
{
dataIndex: 'nickname',
title: '用户名称',
},
{
dataIndex: 'username',
title: '登录账号',
},
{
dataIndex: 'email',
title: '邮箱',
},
{
dataIndex: 'phone',
title: '手机号码',
},
{
dataIndex: 'createdBy',
title: '创建人',
},
{
dataIndex: 'createdAt',
title: '创建时间',
},
{
dataIndex: 'updatedBy',
title: '更新人',
},
{
dataIndex: 'updatedAt',
title: '更新时间',
},
{
title: '操作',
type: 'button',
width: 180,
configable: false,
buttons: [
{
type: 'modify',
text: '修改',
},
{
text: '移动',
disable: () => true,
},
{
type: 'delete',
confirm: '确定删除吗?',
text: '删除',
onClick(props) {
const id = props.record.id;
return api.user.delUser(id);
},
},
],
},
],
source: search => {
return [];
},
search: [
{
field: 'username',
label: '用户名称',
setter: 'input',
},
],
create: {
title: '新增',
width: 580,
items: [
{
field: 'title',
label: '标题',
setter: 'input',
},
{
field: 'title2',
label: '标题',
setter: 'input',
},
{
field: 'title1',
label: '标题',
setter: 'input',
},
],
submit: model => {
return 1;
},
},
modify: {
extend: true,
title: '修改',
},
});
const visible = ref(false);
const videoUrl = 'https://api.dogecloud.com/player/get.mp4?vcode=5ac682e6f8231991&userId=17&ext=.mp4'
const imageUrl =
'https://images.unsplash.com/photo-1682687982468-4584ff11f88a?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxlZGl0b3JpYWwtZmVlZHwxfHx8ZW58MHx8fHx8';
const textUrl = `
1. 是非对错有时只是角度问题 2. 越在意什么什么越会折磨你 3. 反感一个人连听见名字都恶心 4.
温和久了稍有脾气就成了恶人 5. 离去的都是风景留下的才是人生 6. 任何安慰都没有自己看透来的奏效 7.
我也想对每个人好可狼我喂不饱 8. 友情都存在着吃醋更别说爱情了 9. 原有前程可奔赴亦有岁月可回首 10.
偏见来自无非两个地方无知和愚蠢 11. 其实一切的问题时间已经给了答案 12.
所有偷过的懒都会变成打脸的巴掌 13. 信任变得很难假的和真的越来越像 14.
理性的人适合共事感性的人适合共处 15. 没有新故事的人才会对过去念念不忘 16.
你嘴巴那么毒内心一定是有很多苦吧 17. 人生有两次很棒虚惊一场和失而复得 18.
如果觉得生活苦那就给自己撒点糖吧 19. 如果没有见过光明我本可以忍受黑暗 20.
谁心里没有故事只不过是学会了控制 21. 我觉得单身挺好可是一直却招人讽刺 22.
我欣赏你的直言不讳但请管好你的嘴 23. 希望曾经的仰望都能成为以后的日常 24.
心若没有栖息的地方到哪里都是流浪 25. 越长大你越知道有钱比有什么都舒坦 26.
两个人的沟通70%是情绪30%是内容 27. 不发生点烂事永远看不清身边人的模样 28.
对于我们最爱的人不说永远只说珍惜 29. 多少次的义无反顾在时间面前也得认输 30.
我会等因为最好的东西总是压轴出场 31. 幸福是比较级要有东西垫底才感觉的到 32.
在泥泞的道路上要保持一颗玩泥巴的心 33. 这世界上唯一扛得住岁月摧残的就是才华 34.
半山腰总是最挤的所以你得去山顶看看 35. 电影是多么慈祥总是让错过的人重新相逢 36.
并不是因为脆弱而是因为坚强得太久 37. 梦想成真之前它看上去总是那么遥不可及 38.
人生是场荒芜的旅行冷暖自知苦乐在心 39. 如果每个人都理解你那你得普通成什么样 40.
少又不甘多又嫌烦哪有恰到好处的陪伴 41. 听说过很多减肥方法依然胖着过完这一生 42.
我不在意你曾堕落我只在意你是否会崛起 43. 我可以受十分的苦但我受不了半分的委屈 44.
我要做个思想上的女流氓生活上的好菇凉 45. 小时候我们词不达意长大后我们言不由衷 46.
心上的纠葛解的开是结解不开是劫 47. 因为陌生所以勇敢因为距离所以美丽 48.
这世界是如此喧哗让沉默的人显得有点傻 49. 自己生活贫乏的人才喜欢刺探别人的私事 50.
不讲道理是女人的特权发脾气是女人的专利 51. 历史只有人名真的小说只有人名是假的 52.
楼底与楼顶的风景永远不同谁也别羡慕谁 53. 你都从没得到过名利却跟我说什么淡泊名利 54.
人类的极限是由少数人创造的不能一概而论 55. 生活总嘲笑我们太年轻可青春却不经易老去 56.
水凉了还可以喝心凉了连说快乐都显得落寞 57. 我希望躺在向日葵上即使沮丧也能朝着阳光 58.
喜欢发呆的人心里一定有另一个纯净的世界 59. 一个人需要隐藏多少秘密才能巧妙地度过一生 60.
已婚者不安心未婚者不甘心旁观者太热心 61. 长大这两个字连偏旁部首都没有一看就孤独
1. 是非对错有时只是角度问题 2. 越在意什么什么越会折磨你 3. 反感一个人连听见名字都恶心 4.
温和久了稍有脾气就成了恶人 5. 离去的都是风景留下的才是人生 6. 任何安慰都没有自己看透来的奏效 7.
我也想对每个人好可狼我喂不饱 8. 友情都存在着吃醋更别说爱情了 9. 原有前程可奔赴亦有岁月可回首 10.
偏见来自无非两个地方无知和愚蠢 11. 其实一切的问题时间已经给了答案 12.
所有偷过的懒都会变成打脸的巴掌 13. 信任变得很难假的和真的越来越像 14.
理性的人适合共事感性的人适合共处 15. 没有新故事的人才会对过去念念不忘 16.
你嘴巴那么毒内心一定是有很多苦吧 17. 人生有两次很棒虚惊一场和失而复得 18.
如果觉得生活苦那就给自己撒点糖吧 19. 如果没有见过光明我本可以忍受黑暗 20.
谁心里没有故事只不过是学会了控制 21. 我觉得单身挺好可是一直却招人讽刺 22.
我欣赏你的直言不讳但请管好你的嘴 23. 希望曾经的仰望都能成为以后的日常 24.
心若没有栖息的地方到哪里都是流浪 25. 越长大你越知道有钱比有什么都舒坦 26.
两个人的沟通70%是情绪30%是内容 27. 不发生点烂事永远看不清身边人的模样 28.
对于我们最爱的人不说永远只说珍惜 29. 多少次的义无反顾在时间面前也得认输 30.
我会等因为最好的东西总是压轴出场 31. 幸福是比较级要有东西垫底才感觉的到 32.
在泥泞的道路上要保持一颗玩泥巴的心 33. 这世界上唯一扛得住岁月摧残的就是才华 34.
半山腰总是最挤的所以你得去山顶看看 35. 电影是多么慈祥总是让错过的人重新相逢 36.
并不是因为脆弱而是因为坚强得太久 37. 梦想成真之前它看上去总是那么遥不可及 38.
人生是场荒芜的旅行冷暖自知苦乐在心 39. 如果每个人都理解你那你得普通成什么样 40.
少又不甘多又嫌烦哪有恰到好处的陪伴 41. 听说过很多减肥方法依然胖着过完这一生 42.
我不在意你曾堕落我只在意你是否会崛起 43. 我可以受十分的苦但我受不了半分的委屈 44.
我要做个思想上的女流氓生活上的好菇凉 45. 小时候我们词不达意长大后我们言不由衷 46.
心上的纠葛解的开是结解不开是劫 47. 因为陌生所以勇敢因为距离所以美丽 48.
这世界是如此喧哗让沉默的人显得有点傻 49. 自己生活贫乏的人才喜欢刺探别人的私事 50.
不讲道理是女人的特权发脾气是女人的专利 51. 历史只有人名真的小说只有人名是假的 52.
楼底与楼顶的风景永远不同谁也别羡慕谁 53. 你都从没得到过名利却跟我说什么淡泊名利 54.
人类的极限是由少数人创造的不能一概而论 55. 生活总嘲笑我们太年轻可青春却不经易老去 56.
水凉了还可以喝心凉了连说快乐都显得落寞 57. 我希望躺在向日葵上即使沮丧也能朝着阳光 58.
喜欢发呆的人心里一定有另一个纯净的世界 59. 一个人需要隐藏多少秘密才能巧妙地度过一生 60.
已婚者不安心未婚者不甘心旁观者太热心 61. 长大这两个字连偏旁部首都没有一看就孤独
1. 是非对错有时只是角度问题 2. 越在意什么什么越会折磨你 3. 反感一个人连听见名字都恶心 4.
温和久了稍有脾气就成了恶人 5. 离去的都是风景留下的才是人生 6. 任何安慰都没有自己看透来的奏效 7.
我也想对每个人好可狼我喂不饱 8. 友情都存在着吃醋更别说爱情了 9. 原有前程可奔赴亦有岁月可回首 10.
偏见来自无非两个地方无知和愚蠢 11. 其实一切的问题时间已经给了答案 12.
所有偷过的懒都会变成打脸的巴掌 13. 信任变得很难假的和真的越来越像 14.
理性的人适合共事感性的人适合共处 15. 没有新故事的人才会对过去念念不忘 16.
你嘴巴那么毒内心一定是有很多苦吧 17. 人生有两次很棒虚惊一场和失而复得 18.
如果觉得生活苦那就给自己撒点糖吧 19. 如果没有见过光明我本可以忍受黑暗 20.
谁心里没有故事只不过是学会了控制 21. 我觉得单身挺好可是一直却招人讽刺 22.
我欣赏你的直言不讳但请管好你的嘴 23. 希望曾经的仰望都能成为以后的日常 24.
心若没有栖息的地方到哪里都是流浪 25. 越长大你越知道有钱比有什么都舒坦 26.
两个人的沟通70%是情绪30%是内容 27. 不发生点烂事永远看不清身边人的模样 28.
对于我们最爱的人不说永远只说珍惜 29. 多少次的义无反顾在时间面前也得认输 30.
我会等因为最好的东西总是压轴出场 31. 幸福是比较级要有东西垫底才感觉的到 32.
在泥泞的道路上要保持一颗玩泥巴的心 33. 这世界上唯一扛得住岁月摧残的就是才华 34.
半山腰总是最挤的所以你得去山顶看看 35. 电影是多么慈祥总是让错过的人重新相逢 36.
并不是因为脆弱而是因为坚强得太久 37. 梦想成真之前它看上去总是那么遥不可及 38.
人生是场荒芜的旅行冷暖自知苦乐在心 39. 如果每个人都理解你那你得普通成什么样 40.
少又不甘多又嫌烦哪有恰到好处的陪伴 41. 听说过很多减肥方法依然胖着过完这一生 42.
我不在意你曾堕落我只在意你是否会崛起 43. 我可以受十分的苦但我受不了半分的委屈 44.
我要做个思想上的女流氓生活上的好菇凉 45. 小时候我们词不达意长大后我们言不由衷 46.
心上的纠葛解的开是结解不开是劫 47. 因为陌生所以勇敢因为距离所以美丽 48.
这世界是如此喧哗让沉默的人显得有点傻 49. 自己生活贫乏的人才喜欢刺探别人的私事 50.
不讲道理是女人的特权发脾气是女人的专利 51. 历史只有人名真的小说只有人名是假的 52.
楼底与楼顶的风景永远不同谁也别羡慕谁 53. 你都从没得到过名利却跟我说什么淡泊名利 54.
人类的极限是由少数人创造的不能一概而论 55. 生活总嘲笑我们太年轻可青春却不经易老去 56.
水凉了还可以喝心凉了连说快乐都显得落寞 57. 我希望躺在向日葵上即使沮丧也能朝着阳光 58.
喜欢发呆的人心里一定有另一个纯净的世界 59. 一个人需要隐藏多少秘密才能巧妙地度过一生 60.
已婚者不安心未婚者不甘心旁观者太热心 61. 长大这两个字连偏旁部首都没有一看就孤独
1. 是非对错有时只是角度问题 2. 越在意什么什么越会折磨你 3. 反感一个人连听见名字都恶心 4.
温和久了稍有脾气就成了恶人 5. 离去的都是风景留下的才是人生 6. 任何安慰都没有自己看透来的奏效 7.
我也想对每个人好可狼我喂不饱 8. 友情都存在着吃醋更别说爱情了 9. 原有前程可奔赴亦有岁月可回首 10.
偏见来自无非两个地方无知和愚蠢 11. 其实一切的问题时间已经给了答案 12.
所有偷过的懒都会变成打脸的巴掌 13. 信任变得很难假的和真的越来越像 14.
理性的人适合共事感性的人适合共处 15. 没有新故事的人才会对过去念念不忘 16.
你嘴巴那么毒内心一定是有很多苦吧 17. 人生有两次很棒虚惊一场和失而复得 18.
如果觉得生活苦那就给自己撒点糖吧 19. 如果没有见过光明我本可以忍受黑暗 20.
谁心里没有故事只不过是学会了控制 21. 我觉得单身挺好可是一直却招人讽刺 22.
我欣赏你的直言不讳但请管好你的嘴 23. 希望曾经的仰望都能成为以后的日常 24.
心若没有栖息的地方到哪里都是流浪 25. 越长大你越知道有钱比有什么都舒坦 26.
两个人的沟通70%是情绪30%是内容 27. 不发生点烂事永远看不清身边人的模样 28.
对于我们最爱的人不说永远只说珍惜 29. 多少次的义无反顾在时间面前也得认输 30.
我会等因为最好的东西总是压轴出场 31. 幸福是比较级要有东西垫底才感觉的到 32.
在泥泞的道路上要保持一颗玩泥巴的心 33. 这世界上唯一扛得住岁月摧残的就是才华 34.
半山腰总是最挤的所以你得去山顶看看 35. 电影是多么慈祥总是让错过的人重新相逢 36.
并不是因为脆弱而是因为坚强得太久 37. 梦想成真之前它看上去总是那么遥不可及 38.
人生是场荒芜的旅行冷暖自知苦乐在心 39. 如果每个人都理解你那你得普通成什么样 40.
少又不甘多又嫌烦哪有恰到好处的陪伴 41. 听说过很多减肥方法依然胖着过完这一生 42.
我不在意你曾堕落我只在意你是否会崛起 43. 我可以受十分的苦但我受不了半分的委屈 44.
我要做个思想上的女流氓生活上的好菇凉 45. 小时候我们词不达意长大后我们言不由衷 46.
心上的纠葛解的开是结解不开是劫 47. 因为陌生所以勇敢因为距离所以美丽 48.
这世界是如此喧哗让沉默的人显得有点傻 49. 自己生活贫乏的人才喜欢刺探别人的私事 50.
不讲道理是女人的特权发脾气是女人的专利 51. 历史只有人名真的小说只有人名是假的 52.
楼底与楼顶的风景永远不同谁也别羡慕谁 53. 你都从没得到过名利却跟我说什么淡泊名利 54.
人类的极限是由少数人创造的不能一概而论 55. 生活总嘲笑我们太年轻可青春却不经易老去 56.
水凉了还可以喝心凉了连说快乐都显得落寞 57. 我希望躺在向日葵上即使沮丧也能朝着阳光 58.
喜欢发呆的人心里一定有另一个纯净的世界 59. 一个人需要隐藏多少秘密才能巧妙地度过一生 60.
已婚者不安心未婚者不甘心旁观者太热心 61. 长大这两个字连偏旁部首都没有一看就孤独
`;
</script>
<style scoped></style>

File diff suppressed because it is too large Load Diff

View File

@ -5,59 +5,41 @@
</template>
<script setup lang="tsx">
import { api } from '@/api';
import { useTable } from '@/components/AnTable';
import { dayjs } from '@/libs/dayjs';
import { Tag } from '@arco-design/web-vue';
import { Image } from '@arco-design/web-vue';
import data from './data.json';
defineOptions({ name: 'SystemLogoPage' });
const { component: OperationTable } = useTable({
columns: [
{
title: '登陆账号',
dataIndex: 'nickname',
title: '标题',
dataIndex: 'title',
width: 140,
},
{
title: '操作描述',
dataIndex: 'description',
render: ({ record: { status, description } }) => {
render: ({ record }) => {
return (
<span>
<Tag color={status === null || status ? 'green' : 'red'} class="mr-2">
{status === null || status ? '成功' : '失败'}
</Tag>
{description}
</span>
);
},
},
{
title: '登陆地址',
dataIndex: 'ip',
width: 200,
render: ({ record }) => `${record.addr || '未知'}(${record.ip})`,
},
{
title: '操作系统',
dataIndex: 'os',
width: 160,
},
{
title: '浏览器',
dataIndex: 'browser',
width: 160,
},
{
title: '登陆时间',
dataIndex: 'createdAt',
width: 120,
render: ({ record }) => dayjs(record.createdAt).fromNow(),
<div class="flex gap-2">
<div>
<Image width={188} src={record['cover-src']}></Image>
</div>
<div>
<div>
<a-link href={record['title-href']} target="_blank">
{ record.title }
</a-link>
</div>
<div>
<a-link href={record['user-href']}>{record.user}</a-link>
</div>
</div>
</div>
)
}
},
],
source: model => {
return api.log.getLoginLogs(model);
return data;
},
search: [
{

View File

@ -1,6 +1,6 @@
import { NProgress } from '@/libs/nprogress';
import { useAppStore } from '@/store';
import { Router } from 'vue-router';
import { NProgress } from "@/libs/nprogress";
import { useAppStore } from "@/store";
import { Router } from "vue-router";
const routeMap = new Map<string, boolean>();

View File

@ -36,7 +36,7 @@ function routesToItems(routes: RouteRecordRaw[]): MenuItem[] {
id = `${path}/index`;
parentId = path;
items.push({
title,
title,
icon,
sort,
path,

View File

@ -25,6 +25,7 @@ declare module '@vue/runtime-core' {
AEmpty: typeof import('@arco-design/web-vue')['Empty']
AForm: typeof import('@arco-design/web-vue')['Form']
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
AImage: typeof import('@arco-design/web-vue')['Image']
AImagePreview: typeof import('@arco-design/web-vue')['ImagePreview']
AImagePreviewGroup: typeof import('@arco-design/web-vue')['ImagePreviewGroup']
AInput: typeof import('@arco-design/web-vue')['Input']
@ -42,6 +43,7 @@ declare module '@vue/runtime-core' {
AnEmpty: typeof import('./../components/AnEmpty/AnEmpty.vue')['default']
AnForbidden: typeof import('./../components/AnForbidden/AnForbidden.vue')['default']
AnToast: typeof import('./../components/AnToast/AnToast.vue')['default']
AnViewer: typeof import('./../components/AnViewer/AnViewer.vue')['default']
APagination: typeof import('@arco-design/web-vue')['Pagination']
APopover: typeof import('@arco-design/web-vue')['Popover']
AProgress: typeof import('@arco-design/web-vue')['Progress']
@ -49,6 +51,7 @@ declare module '@vue/runtime-core' {
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
ASelect: typeof import('@arco-design/web-vue')['Select']
ASlider: typeof import('@arco-design/web-vue')['Slider']
ASpace: typeof import('@arco-design/web-vue')['Space']
ASpin: typeof import('@arco-design/web-vue')['Spin']
ASwitch: typeof import('@arco-design/web-vue')['Switch']

View File

@ -12,4 +12,9 @@ declare module "numeral" {
export default numeral;
}
declare module 'dplayer' {
const dp: any;
export default dp;
}
type Recordable = Record<string, any>;