Compare commits

..

13 Commits

Author SHA1 Message Date
chenchun
998d97b669 fix: 修复审计日志问题 2024-10-21 16:58:19 +08:00
chenchun
453d95a460 feat: 新增验证 2024-10-21 10:39:16 +08:00
chenchun
d55545849a fix: 修复人数访问 2024-10-18 13:14:51 +08:00
橙子
22ba44c271 feat: 完善部门编号字段 2024-10-15 23:26:18 +08:00
橙子
ae2cc7ad9b feat: 优化访问数量统计,采用本地缓存+分布式缓存+数据库 2024-10-15 23:07:12 +08:00
橙子
c880f32d33 !70 修复菜单编辑新增bug
Merge pull request !70 from fuxing168/Fyun168
2024-10-15 10:38:30 +00:00
志福
7b20b68b6a 修复菜单管理里面,编辑后或者新增没有增加routerName,会导致主菜单导航时无法显示 2024-10-15 18:31:14 +08:00
橙子
974f264272 !67 修复字段名驼峰转下划线导致查询日志列表失败的兼容问题
Merge pull request !67 from 凤凰/abp
2024-10-15 01:26:04 +00:00
凤凰
bcbb2b5139 fix: 修复字段名驼峰转下划线导致查询日志列表失败的兼容问题 2024-10-15 00:25:43 +08:00
橙子
e09aaa2dc7 !65 增加表单编辑器
Merge pull request !65 from 李大饼/abp
2024-10-14 08:58:56 +00:00
橙子
e3178d7579 !66 Ruoyi是否启用验证码由后台appsettings.json决定
Merge pull request !66 from Po/abp
2024-10-14 08:57:55 +00:00
Po
b59dfbc3fd Ruoyi是否启用验证码由后台appsettings.json决定 2024-10-14 16:49:33 +08:00
simiyu
0f21688b3c feat:添加表单生成器 2024-10-14 15:54:47 +08:00
30 changed files with 208 additions and 432 deletions

View File

@@ -109,6 +109,7 @@ namespace Yi.Framework.AuditLogging.Domain.Entities
public virtual string? Url { get; protected set; }
[SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString)]
public virtual string? Exceptions { get; protected set; }
public virtual string? Comments { get; protected set; }

View File

@@ -4,7 +4,9 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Yi.Framework.Bbs.Domain.Shared.Caches;
using Yi.Framework.Bbs.Domain.Shared.Etos;
namespace Yi.Framework.Bbs.Application.Extensions;
@@ -14,6 +16,29 @@ namespace Yi.Framework.Bbs.Application.Extensions;
/// 需考虑一致性问题,又不能上锁影响性能
/// </summary>
public class AccessLogMiddleware : IMiddleware, ITransientDependency
{
private static int _accessLogNumber = 0;
internal static void ResetAccessLogNumber()
{
_accessLogNumber = 0;
}
internal static int GetAccessLogNumber()
{
return _accessLogNumber;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
await next(context);
Interlocked.Increment(ref _accessLogNumber);
}
}
public class AccessLogResetEventHandler : ILocalEventHandler<AccessLogResetArgs>,
ITransientDependency
{
/// <summary>
/// 缓存前缀
@@ -39,13 +64,28 @@ public class AccessLogMiddleware : IMiddleware, ITransientDependency
return redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled);
}
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
//该事件由job定时10秒触发
public async Task HandleEventAsync(AccessLogResetArgs eventData)
{
await next(context);
if (EnableRedisCache)
{
await RedisClient.IncrByAsync($"{CacheKeyPrefix}:{AccessLogCacheConst.Key}:{DateTime.Now.Date}", 1);
//分布式锁
if (await RedisClient.SetNxAsync("AccessLogLock",true,TimeSpan.FromSeconds(5)))
{
//自增长数
var incrNumber= AccessLogMiddleware.GetAccessLogNumber();
//立即重置,开始计算,方式丢失
AccessLogMiddleware.ResetAccessLogNumber();
if (incrNumber>0)
{
await RedisClient.IncrByAsync(
$"{CacheKeyPrefix}{AccessLogCacheConst.Key}:{DateTime.Now.Date:yyyyMMdd}", incrNumber);
}
}
}
}
}

View File

@@ -0,0 +1,38 @@
using FreeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Quartz;
using Volo.Abp.BackgroundWorkers.Quartz;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Shared.Caches;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Application.Jobs;
public class AccessLogCacheJob : QuartzBackgroundWorkerBase
{
private readonly ILocalEventBus _localEventBus;
public AccessLogCacheJob(ILocalEventBus localEventBus)
{
_localEventBus = localEventBus;
JobDetail = JobBuilder.Create<AccessLogCacheJob>().WithIdentity(nameof(AccessLogCacheJob))
.Build();
//每10秒执行一次将本地缓存转入redis防止丢数据
Trigger = TriggerBuilder.Create().WithIdentity(nameof(AccessLogCacheJob))
.WithSimpleSchedule((schedule) => { schedule.WithInterval(TimeSpan.FromSeconds(10)).RepeatForever();; })
.Build();
}
public override async Task Execute(IJobExecutionContext context)
{
await _localEventBus.PublishAsync(new AccessLogResetArgs());
}
}

View File

@@ -62,7 +62,7 @@ public class AccessLogStoreJob : QuartzBackgroundWorkerBase
{
//当天的访问量
var number =
await RedisClient.GetAsync<long>($"{CacheKeyPrefix}:{AccessLogCacheConst.Key}:{DateTime.Now.Date}");
await RedisClient.GetAsync<long>($"{CacheKeyPrefix}{AccessLogCacheConst.Key}:{DateTime.Now.Date:yyyyMMdd}");
var entity = await _repository._DbQueryable.Where(x => x.AccessLogType == AccessLogTypeEnum.Request)
@@ -81,7 +81,7 @@ public class AccessLogStoreJob : QuartzBackgroundWorkerBase
}
//删除前一天的缓存
await RedisClient.DelAsync($"{CacheKeyPrefix}:{AccessLogCacheConst.Key}:{DateTime.Now.Date.AddDays(-1)}");
await RedisClient.DelAsync($"{CacheKeyPrefix}{AccessLogCacheConst.Key}:{DateTime.Now.Date.AddDays(-1):yyyyMMdd}");
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Yi.Framework.Bbs.Domain.Shared.Etos;
public class AccessLogResetArgs
{
}

View File

@@ -4,5 +4,7 @@
{
public Guid Uuid { get; set; } = Guid.Empty;
public byte[] Img { get; set; }
public bool IsEnableCaptcha { get; set; }
}
}

View File

@@ -3,5 +3,9 @@
public class PhoneCaptchaImageDto
{
public string Phone { get; set; }
public string? Uuid { get; set; }
public string? Code { get; set; }
}
}

View File

@@ -20,7 +20,7 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Menu
public string? Remark { get; set; }
public string? Component { get; set; }
public string? Query { get; set; }
public string? RouterName { get; set; }
public int OrderNum { get; set; }
//public List<MenuEntity>? Children { get; set; }
}

View File

@@ -24,6 +24,8 @@ namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Menu
public int OrderNum { get; set; }
public string? RouterName { get; set; }
//public List<MenuEntity>? Children { get; set; }
}
}

View File

@@ -82,12 +82,12 @@ namespace Yi.Framework.Rbac.Application.Services
/// 校验图片登录验证码,无需和账号绑定
/// </summary>
[AllowAnonymous]
private void ValidationImageCaptcha(LoginInputVo input)
private void ValidationImageCaptcha(string? uuid,string? code )
{
if (_rbacOptions.EnableCaptcha)
{
//登录不想要验证码 ,可不校验
if (!_captcha.Validate(input.Uuid, input.Code))
if (!_captcha.Validate(uuid, code))
{
throw new UserFriendlyException("验证码错误");
}
@@ -109,7 +109,7 @@ namespace Yi.Framework.Rbac.Application.Services
}
//校验验证码
ValidationImageCaptcha(input);
ValidationImageCaptcha(input.Uuid,input.Code);
UserAggregateRoot user = new();
//校验
@@ -157,7 +157,8 @@ namespace Yi.Framework.Rbac.Application.Services
{
var uuid = _guidGenerator.Create();
var captcha = _captcha.Generate(uuid.ToString());
return new CaptchaImageDto { Img = captcha.Bytes, Uuid = uuid };
var enableCaptcha = _rbacOptions.EnableCaptcha;
return new CaptchaImageDto { Img = captcha.Bytes, Uuid = uuid,IsEnableCaptcha= enableCaptcha };
}
/// <summary>
@@ -198,12 +199,15 @@ namespace Yi.Framework.Rbac.Application.Services
}
/// <summary>
/// 手机验证码
/// 手机验证码-需通过图形验证码
/// </summary>
/// <returns></returns>
private async Task<object> PostCaptchaPhoneAsync(ValidationPhoneTypeEnum validationPhoneType,
PhoneCaptchaImageDto input)
{
//验证uuid 和 验证码
ValidationImageCaptcha(input.Uuid,input.Code);
await ValidationPhone(input.Phone);
//注册的手机号验证,是不能已经注册过的

View File

@@ -20,12 +20,12 @@ namespace Yi.Framework.Rbac.Application.Services.RecordLog
public override async Task<PagedResultDto<LoginLogGetListOutputDto>> GetListAsync(LoginLogGetListInputVo input)
{
RefAsync<int> total = 0;
if (input.Sorting.IsNullOrWhiteSpace())
input.Sorting = $"{nameof(LoginLogAggregateRoot.CreationTime)} Desc";
//if (input.Sorting.IsNullOrWhiteSpace())
// input.Sorting = $"{nameof(LoginLogAggregateRoot.CreationTime)} Desc";
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.LoginIp), x => x.LoginIp.Contains(input.LoginIp!))
.WhereIF(!string.IsNullOrEmpty(input.LoginUser), x => x.LoginUser!.Contains(input.LoginUser!))
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.OrderBy(input.Sorting)
.OrderByDescending(it => it.CreationTime) //降序
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<LoginLogGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}

View File

@@ -24,12 +24,12 @@ namespace Yi.Framework.Rbac.Application.Services.RecordLog
public override async Task<PagedResultDto<OperationLogGetListOutputDto>> GetListAsync(OperationLogGetListInputVo input)
{
RefAsync<int> total = 0;
if (input.Sorting.IsNullOrWhiteSpace())
input.Sorting = $"{nameof(OperationLogEntity.CreationTime)} Desc";
//if (input.Sorting.IsNullOrWhiteSpace())
// input.Sorting = $"{nameof(OperationLogEntity.CreationTime)} Desc";
var entities = await _repository._DbQueryable.WhereIF(!string.IsNullOrEmpty(input.OperUser), x => x.OperUser.Contains(input.OperUser!))
.WhereIF(input.OperType is not null, x => x.OperType == input.OperType)
.WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.OrderBy(input.Sorting)
.OrderByDescending(it => it.CreationTime) //降序
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return new PagedResultDto<OperationLogGetListOutputDto>(total, await MapToGetListOutputDtosAsync(entities));
}

View File

@@ -63,12 +63,12 @@ namespace Yi.Framework.Rbac.Domain.Entities
/// <summary>
/// 部门名称
///</summary>
public string DeptName { get; set; } = string.Empty;
public string DeptName { get; set; }
/// <summary>
/// 部门编码
///</summary>
[SugarColumn(ColumnName = "DeptCode")]
public string DeptCode { get; set; } = string.Empty;
public string DeptCode { get; set; }
/// <summary>
/// 负责人
///</summary>

View File

@@ -41,7 +41,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot shenzhenDept = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "SZ",
DeptName = "深圳总公司",
OrderNum = 100,
IsDeleted = false,
@@ -52,7 +52,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot jiangxiDept = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "JX",
DeptName = "江西总公司",
OrderNum = 100,
IsDeleted = false,
@@ -64,7 +64,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot szDept1 = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "YF",
DeptName = "研发部门",
OrderNum = 100,
IsDeleted = false,
@@ -74,7 +74,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot szDept2 = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "SC",
DeptName = "市场部门",
OrderNum = 100,
IsDeleted = false,
@@ -84,7 +84,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot szDept3 = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "CS",
DeptName = "测试部门",
OrderNum = 100,
IsDeleted = false,
@@ -94,7 +94,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot szDept4 = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "CW",
DeptName = "财务部门",
OrderNum = 100,
IsDeleted = false,
@@ -104,7 +104,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot szDept5 = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "YW",
DeptName = "运维部门",
OrderNum = 100,
IsDeleted = false,
@@ -115,7 +115,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot jxDept1 = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "SC",
DeptName = "市场部门",
OrderNum = 100,
IsDeleted = false,
@@ -126,7 +126,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
DeptAggregateRoot jxDept2 = new DeptAggregateRoot(_guidGenerator.Create())
{
DeptCode = "CW2",
DeptName = "财务部门",
OrderNum = 100,
IsDeleted = false,

View File

@@ -227,7 +227,22 @@ namespace Yi.Framework.Rbac.SqlSugarCore.DataSeeds
IsDeleted = false,
};
entities.Add(swagger);
//表单构建
MenuAggregateRoot builder = new MenuAggregateRoot(_guidGenerator.Create(), tool.Id)
{
MenuName = "表单生成器",
MenuType = MenuTypeEnum.Menu,
Router = "build",
IsShow = true,
IsLink = false,
MenuIcon = "form",
Component = "tool/build/index",
IsCache = true,
OrderNum = 101,
IsDeleted = false,
};
entities.Add(builder);
// //ERP
// MenuAggregateRoot erp = new MenuAggregateRoot(_guidGenerator.Create())

View File

@@ -91,13 +91,28 @@ defineExpose({ getRef });
<re-col
v-show="newFormInline.menuType !== 2"
:value="24"
:xs="24"
:sm="24"
:xs="12"
:sm="12"
>
<el-form-item label="菜单图标">
<IconSelect v-model="newFormInline.menuIcon" class="w-full" />
</el-form-item>
</re-col>
<re-col
v-show="newFormInline.menuType !== 2"
:value="24"
:xs="12"
:sm="12"
>
<el-form-item label="路由名称">
<el-input
v-model="newFormInline.routerName"
clearable
placeholder="请输入菜单名称"
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="12" :xs="24" :sm="24">
<el-form-item label="菜单名称" prop="menuName">
<el-input

View File

@@ -89,7 +89,7 @@ export function useMenu() {
{
label: "显示",
prop: "isShow",
formatter: ({ isShow }) => (isShow ? "" : ""),
formatter: ({ isShow }) => (isShow ? "" : ""),
width: 100
},
{

View File

@@ -32,7 +32,8 @@
"typeface-roboto": "^1.1.13",
"vue": "3.4.21",
"vue-cropper": "1.0.3",
"vue-router": "4.3.0"
"vue-router": "4.3.0",
"vform3-builds": "^3.0.10"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.4",

View File

@@ -11,6 +11,8 @@ import App from './App'
import store from './store'
import router from './router'
import directive from './directive' // directive
import VForm3 from 'vform3-builds'
import 'vform3-builds/dist/designer.style.css' //引入VForm3样式
// 注册指令
@@ -77,5 +79,6 @@ app.use(ElementPlus, {
// 支持 large、default、small
size: Cookies.get('size') || 'default'
})
app.use(VForm3)
app.mount('#app')

View File

@@ -113,7 +113,7 @@ const loginRules = {
const codeUrl = ref("");
const loading = ref(false);
// 验证码开关
const captchaEnabled = ref(true);
const captchaEnabled = ref(false);
// 注册开关
const register = ref(false);
const redirect = ref(undefined);
@@ -157,7 +157,7 @@ function handleLogin() {
function getCode() {
getCodeImg().then(res => {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
captchaEnabled.value = res.data.isEnableCaptcha === undefined ? true : res.data.isEnableCaptcha;
if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + res.data.img;
loginForm.value.uuid = res.data.uuid;

View File

@@ -142,7 +142,7 @@ function handleRegister() {
function getCode() {
getCodeImg().then(res => {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
captchaEnabled.value = res.data.isEnableCaptcha === undefined ? true : res.data.isEnableCaptcha;
if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + res.img;
registerForm.value.uuid = res.uuid;

View File

@@ -9,6 +9,14 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="部门编号" prop="deptCode">
<el-input
v-model="queryParams.deptCode"
placeholder="请输入部门编号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="state">
<el-select v-model="queryParams.state" placeholder="部门状态" clearable>
<el-option
@@ -55,6 +63,7 @@
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column prop="deptName" label="部门名称" width="260"></el-table-column>
<el-table-column prop="deptCode" label="部门编号" width="200"></el-table-column>
<el-table-column prop="orderNum" label="排序" width="200"></el-table-column>
<el-table-column prop="state" label="状态" width="100">
<template #default="scope">
@@ -112,6 +121,11 @@
<el-input v-model="form.deptName" placeholder="请输入部门名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门编号" prop="deptCode">
<el-input v-model="form.deptCode" placeholder="部门编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="显示排序" prop="orderNum">
<el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
@@ -173,11 +187,13 @@ const refreshTable = ref(true);
const data = reactive({
form: {},
queryParams: {
deptCode:undefined,
deptName: undefined,
state: undefined
},
rules: {
parentId: [{ required: true, message: "上级部门不能为空", trigger: "blur" }],
deptCode: [{ required: true, message: "部门编号不能为空", trigger: "blur" }],
deptName: [{ required: true, message: "部门名称不能为空", trigger: "blur" }],
orderNum: [{ required: true, message: "显示排序不能为空", trigger: "blur" }],
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],

View File

@@ -1,3 +1,25 @@
<template>
<div> 表单构建 <svg-icon icon-class="build" /> </div>
</template>
<div ref="box">
<v-form-designer ></v-form-designer>
</div>
</template>
<script setup lang="ts">
import {ref,onMounted} from "vue";
const box = ref<Element>()
onMounted(() =>
{
box.value?.firstChild?.classList.add("not-margin")
box.value?.firstChild?.children[0].remove()
})
</script>
<style lang="scss" scoped>
div {
margin: 0; /* 如果页面出现垂直滚动条则加入此行CSS以消除之 */
}
.not-margin{
margin: 0 !important;
}
</style>

View File

@@ -1,21 +0,0 @@
<script setup>
</script>
<template>
<div class="body">
<div class="head"></div>
<div class="content">
<router-view></router-view>
<router-view name="market"></router-view>
</div>
<div class="bottom"></div>
</div>
</template>
<style scoped>
.body{
display: flex;
background-color: #cdcdcd;
}
</style>

View File

@@ -1,12 +0,0 @@
<template>
<div class="page-body">
</div>
</template>
<style scoped>
.page-body{
min-height: 1000px;
min-width: 500px;
background-color: #1482f0;
}
</style>

View File

@@ -1,33 +0,0 @@
import { createRouter, createWebHistory } from "vue-router";
import Layout from "../layout/Index.vue";
// import NotFound from "../views/error/404.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
scrollBehavior(to, from, savedPosition) {
// 始终滚动到顶部
return { top: 0 };
},
routes: [
{
path: "/",
name: "layout",
component: Layout,
redirect: "/index",
children: [
{
name: "index",
path: "/index",
components:{
default: ()=>import("../views/home/Index.vue"),
market: ()=>import("../views/market/Index.vue"),
}
},
]
}
// { path: "/:pathMatch(.*)*", name: "NotFound", component: NotFound },
],
});
export default router;

View File

@@ -1,85 +0,0 @@
<template>
<div class="errPage-container">
<el-button icon="arrow-left" class="pan-back-btn" @click="back">
返回
</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">
401错误!
</h1>
<h2>您没有访问权限</h2>
<h6>对不起您没有访问权限请不要进行非法操作您可以返回主页面</h6>
<ul class="list-unstyled">
<li class="link-type">
<router-link to="/">
回首页
</router-link>
</li>
</ul>
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
</div>
</template>
<script setup>
import errImage from "@/assets/401_images/401.gif";
import { useRouter } from "vue-router";
let { proxy } = getCurrentInstance();
const router=useRouter();
const errGif = ref(errImage + "?" + +new Date());
function back() {
if (proxy.$route.query.noGoBack) {
router.push({ path: "/" });
} else {
router.go(-1);
}
}
</script>
<style lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none !important;
}
.pan-gif {
margin: 0 auto;
display: block;
}
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
font-weight: 700;
color: #484848;
}
.list-unstyled {
font-size: 14px;
li {
padding-bottom: 5px;
}
a {
color: #008489;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

View File

@@ -1,228 +0,0 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">
404错误!
</div>
<div class="bullshit__headline">
{{ message }}
</div>
<div class="bullshit__info">
对不起您正在寻找的页面不存在尝试检查URL的错误然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容
</div>
<router-link to="/index" class="bullshit__return-home">
返回首页
</router-link>
</div>
</div>
</div>
</template>
<script setup>
import {computed} from 'vue'
let message = computed(() => {
return '找不到网页!'
})
</script>
<style lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@@ -1,3 +0,0 @@
<template>
首页大西瓜
</template>

View File

@@ -1,11 +0,0 @@
<script setup>
</script>
<template>
市场
</template>
<style scoped>
</style>