Merge branch 'abp-dev' of https://gitee.com/ccnetcore/Yi into abp-dev
This commit is contained in:
@@ -44,6 +44,21 @@ namespace Yi.Framework.Bbs.Application.Services
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取全部访问流量
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<AccessLogDto>> Get()
|
||||
{
|
||||
var entities = await _repository._DbQueryable.OrderBy(x => x.CreationTime).ToListAsync();
|
||||
var output = entities.Adapt<List<AccessLogDto>>();
|
||||
output?.ForEach(x => x.CreationTime = x.CreationTime.Date);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发
|
||||
/// </summary>
|
||||
@@ -106,5 +121,8 @@ namespace Yi.Framework.Bbs.Application.Services
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,5 +93,6 @@ namespace Yi.Framework.Bbs.Application.Services.Analyses
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.EventBus.Local;
|
||||
using Yi.Framework.Bbs.Application.Contracts.IServices;
|
||||
using Yi.Framework.Bbs.Domain.Managers;
|
||||
using Yi.Framework.Bbs.Domain.Shared.Etos;
|
||||
|
||||
namespace Yi.Framework.Bbs.Application.Services.Integral
|
||||
@@ -9,7 +11,8 @@ namespace Yi.Framework.Bbs.Application.Services.Integral
|
||||
public class LuckyService : ApplicationService
|
||||
{
|
||||
private ILocalEventBus _localEventBus;
|
||||
public LuckyService(ILocalEventBus localEventBus) { _localEventBus = localEventBus; }
|
||||
private BbsUserManager _bbsUserManager;
|
||||
public LuckyService(ILocalEventBus localEventBus, BbsUserManager bbsUserManager) { _bbsUserManager = bbsUserManager; _localEventBus = localEventBus; }
|
||||
|
||||
/// <summary>
|
||||
/// 大转盘
|
||||
@@ -18,9 +21,22 @@ namespace Yi.Framework.Bbs.Application.Services.Integral
|
||||
[Authorize]
|
||||
public async Task<int> PostWheel()
|
||||
{
|
||||
int[] values=new int[10] { 0,10,30,50,60,80,90,100,200,666};
|
||||
var userInfo = await _bbsUserManager.GetBbsUserInfoAsync(CurrentUser.Id.Value);
|
||||
if (userInfo.Money < 50)
|
||||
{
|
||||
throw new UserFriendlyException("钱钱不足!");
|
||||
}
|
||||
|
||||
|
||||
int[] values = new int[10] { 0, 10, 30, 50, 60, 80, 90, 100, 200, 666 };
|
||||
var index = GetWheelIndex();
|
||||
var value = values[index]-50;
|
||||
var value = values[index] - 50;
|
||||
|
||||
////不存在负数钱钱
|
||||
//if (value < 0)
|
||||
//{
|
||||
// value = 0;
|
||||
//}
|
||||
|
||||
//修改钱钱,如果钱钱不足,直接会丢出去,那本次抽奖将无效
|
||||
await _localEventBus.PublishAsync(new MoneyChangeEventArgs { UserId = CurrentUser.Id!.Value, Number = value }, false);
|
||||
|
||||
@@ -269,6 +269,7 @@ namespace Yi.Framework.Rbac.Application.Services
|
||||
}
|
||||
//此处优先从缓存中获取
|
||||
UserRoleMenuDto output = null;
|
||||
|
||||
var cacheData = await _userCache.GetAsync(new UserInfoCacheKey(userId.Value));
|
||||
if (cacheData is not null)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Dtos
|
||||
/// <summary>
|
||||
/// 主键
|
||||
/// </summary>
|
||||
public Guid Id { get; protected set; }
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 逻辑删除
|
||||
@@ -134,7 +134,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Dtos
|
||||
}
|
||||
public class RoleDto
|
||||
{
|
||||
public Guid Id { get; protected set; }
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 逻辑删除
|
||||
@@ -194,7 +194,7 @@ namespace Yi.Framework.Rbac.Domain.Shared.Dtos
|
||||
}
|
||||
public class MenuDto
|
||||
{
|
||||
public Guid Id { get; protected set; }
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 逻辑删除
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Yi.Framework.Rbac.SqlSugarCore.Repositories
|
||||
var user = await _DbQueryable.Includes(u => u.Roles.Where(r => r.IsDeleted == false).ToList(), r => r.Menus.Where(m => m.IsDeleted == false).ToList()).InSingleAsync(userId);
|
||||
if (user is null)
|
||||
{
|
||||
throw new ArgumentNullException($"参数错误,用户id:{nameof(userId)} 不存在");
|
||||
throw new UserFriendlyException($"数据错误,用户id:{nameof(userId)} 不存在,请重新登录");
|
||||
}
|
||||
user.Password = string.Empty;
|
||||
user.Salt = string.Empty;
|
||||
|
||||
@@ -15,3 +15,10 @@ export function getWeek() {
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
// 获取全部数据
|
||||
export function getAccessList() {
|
||||
return request({
|
||||
url: "/access-log",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
96
Yi.Bbs.Vue3/src/assets/atom-one-dark.css
Normal file
96
Yi.Bbs.Vue3/src/assets/atom-one-dark.css
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
|
||||
Atom One Dark by Daniel Gamage
|
||||
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
|
||||
|
||||
base: #282c34
|
||||
mono-1: #abb2bf
|
||||
mono-2: #818896
|
||||
mono-3: #5c6370
|
||||
hue-1: #56b6c2
|
||||
hue-2: #61aeee
|
||||
hue-3: #c678dd
|
||||
hue-4: #98c379
|
||||
hue-5: #e06c75
|
||||
hue-5-2: #be5046
|
||||
hue-6: #d19a66
|
||||
hue-6-2: #e6c07b
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #abb2bf;
|
||||
background: #282c34;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #5c6370;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-doctag,
|
||||
.hljs-keyword,
|
||||
.hljs-formula {
|
||||
color: #c678dd;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name,
|
||||
.hljs-selector-tag,
|
||||
.hljs-deletion,
|
||||
.hljs-subst {
|
||||
color: #e06c75;
|
||||
}
|
||||
|
||||
.hljs-literal {
|
||||
color: #56b6c2;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-regexp,
|
||||
.hljs-addition,
|
||||
.hljs-attribute,
|
||||
.hljs-meta-string {
|
||||
color: #98c379;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-class .hljs-title {
|
||||
color: #e6c07b;
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-type,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-number {
|
||||
color: #d19a66;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-link,
|
||||
.hljs-meta,
|
||||
.hljs-selector-id,
|
||||
.hljs-title {
|
||||
color: #61aeee;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -1,3 +1,71 @@
|
||||
<style scoped lang="scss">
|
||||
/*::v-dedp .markdown-body pre{
|
||||
padding: 0 !important;
|
||||
}*/
|
||||
::v-deep .pre-out
|
||||
{
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
::v-deep .pre {
|
||||
padding: 0;
|
||||
margin-bottom: 0;
|
||||
overflow-x: hidden;
|
||||
.header {
|
||||
background-color: #409eff;
|
||||
color: white;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 10px;
|
||||
|
||||
|
||||
.language {}
|
||||
.copy:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.copy {
|
||||
margin: 0px 10px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.code-con {
|
||||
display: flex;
|
||||
|
||||
.nav {
|
||||
display: block;
|
||||
background-color: #282C34;
|
||||
|
||||
}
|
||||
|
||||
.code {
|
||||
display: block;
|
||||
padding: 10px 10px;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
::v-deep .nav-ul {
|
||||
border-right: 1px solid #FFFFFF;
|
||||
margin-top: 12px;
|
||||
padding-left: 10px;
|
||||
padding-right: 2px;
|
||||
|
||||
.nav-li {
|
||||
margin: 5.3px 0;
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div>
|
||||
<div class="markdown-body" v-html="outputHtml"></div>
|
||||
@@ -9,24 +77,87 @@ import { marked } from 'marked';
|
||||
|
||||
import hljs from "highlight.js";
|
||||
//可以设置加载样式切换主题
|
||||
import 'highlight.js/styles/atom-one-dark.css'
|
||||
import '@/assets/atom-one-dark.css'
|
||||
import '@/assets/github-markdown.css'
|
||||
import { ref,watch } from 'vue';
|
||||
import {nextTick , ref, watch } from 'vue';
|
||||
const BREAK_LINE_REGEXP = /\r\n|\r|\n/g;
|
||||
|
||||
|
||||
|
||||
const outputHtml=ref("")
|
||||
const outputHtml = ref("")
|
||||
const props = defineProps(['code'])
|
||||
watch(props,(n,o)=>{
|
||||
let codeCopyDic=[];
|
||||
const addCopyEvent=()=>{
|
||||
const copySpans = document.querySelectorAll('.copy');
|
||||
console.log(copySpans,"copySpans");
|
||||
// 为每个 copy span 元素添加点击事件
|
||||
copySpans.forEach(span => {
|
||||
|
||||
span.addEventListener('click', async function() {
|
||||
await navigator.clipboard.writeText(codeCopyDic.filter(x=>x.id==span.id)[0].code );
|
||||
ElMessage({
|
||||
message: "代码块复制成功",
|
||||
type: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//code部分处理、高亮
|
||||
const codeHandler = (code, language) => {
|
||||
const codeIndex = parseInt(Date.now() + "") + Math.floor(Math.random() * 10000000);
|
||||
// 格式化第一行是右侧language和 “复制” 按钮;
|
||||
if (code) {
|
||||
const navCode = navHandler(code)
|
||||
try {
|
||||
// 使用 highlight.js 对代码进行高亮显示
|
||||
const preCode = hljs.highlightAuto(code).value;
|
||||
// 将代码包裹在 textarea 中,由于防止textarea渲染出现问题,这里将 "<" 用 "<" 代替,不影响复制功能
|
||||
let html = `<pre class='hljs pre'><div class="header"><span class="language">${language}</span><span class="copy" id="${codeIndex}">复制代码</span></div><div class="code-con"><div class="nav">${navCode}</div><code class="code">${preCode}</code></div></pre>`;
|
||||
codeCopyDic.push({id: codeIndex,code:code});
|
||||
console.log(codeCopyDic.length);
|
||||
return html;
|
||||
//<textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy${codeIndex}">${code.replace(/<\/textarea>/g, "</textarea>")}</textarea>
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//左侧导航栏处理
|
||||
const navHandler = (code) => {
|
||||
//获取行数
|
||||
var linesCount = getLinesCount(code);
|
||||
|
||||
var currentLine = 1;
|
||||
var liHtml = ``;
|
||||
while (linesCount + 1 >= currentLine) {
|
||||
liHtml += `<li class="nav-li">${currentLine}</li>`
|
||||
currentLine++
|
||||
}
|
||||
|
||||
let html = `<ul class="nav-ul">${liHtml}</ul>`
|
||||
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
const getLinesCount = (text) => {
|
||||
return (text.trim().match(BREAK_LINE_REGEXP) || []).length;
|
||||
}
|
||||
|
||||
watch(props, (n, o) => {
|
||||
codeCopyDic=[];
|
||||
marked.setOptions({
|
||||
renderer: new marked.Renderer(),
|
||||
highlight: function(code) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
},
|
||||
highlight: function (code, language) {
|
||||
return codeHandler(code, language);
|
||||
// return hljs.highlightAuto(code).value;
|
||||
},
|
||||
pedantic: false,
|
||||
gfm: true,//允许 Git Hub标准的markdown
|
||||
tables: true,//支持表格
|
||||
|
||||
|
||||
breaks: true,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
@@ -36,8 +167,11 @@ watch(props,(n,o)=>{
|
||||
}
|
||||
);
|
||||
//需要注意代码块样式
|
||||
outputHtml.value = marked(n.code).replace(/<pre>/g, "<pre class='hljs'>")
|
||||
},{immediate:true,deep:true})
|
||||
|
||||
const soureHtml = marked(n.code);
|
||||
outputHtml.value= soureHtml.replace(/<pre>/g, '<pre class="pre-out">');
|
||||
nextTick(()=>{
|
||||
addCopyEvent();
|
||||
})
|
||||
}, { immediate: true, deep: true })
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps(["items", "header", "text", "hideDivider", "height"]);
|
||||
const emit = defineEmits(['onClickText'])
|
||||
const height = ref(props.height + "px");
|
||||
|
||||
const onClickText=()=>{
|
||||
emit('onClickText')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -10,7 +15,7 @@ const height = ref(props.height + "px");
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ props.header }}</span>
|
||||
<el-link :underline="false" type="primary">{{ props.text }}</el-link>
|
||||
<el-link :underline="false" type="primary" @click="onClickText">{{ props.text }}</el-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -6,22 +6,11 @@
|
||||
<ScrollbarInfo />
|
||||
</div>
|
||||
<el-row class="left-div">
|
||||
<el-col
|
||||
:span="8"
|
||||
v-for="i in plateList"
|
||||
:key="i.id"
|
||||
class="plate"
|
||||
:style="{
|
||||
'padding-left': i % 3 == 1 ? 0 : 0.2 + 'rem',
|
||||
'padding-right': i % 3 == 0 ? 0 : 0.2 + 'rem',
|
||||
}"
|
||||
>
|
||||
<PlateCard
|
||||
:name="i.name"
|
||||
:introduction="i.introduction"
|
||||
:id="i.id"
|
||||
:isPublish="i.isDisableCreateDiscuss"
|
||||
/>
|
||||
<el-col :span="8" v-for="i in plateList" :key="i.id" class="plate" :style="{
|
||||
'padding-left': i % 3 == 1 ? 0 : 0.2 + 'rem',
|
||||
'padding-right': i % 3 == 0 ? 0 : 0.2 + 'rem',
|
||||
}">
|
||||
<PlateCard :name="i.name" :introduction="i.introduction" :id="i.id" :isPublish="i.isDisableCreateDiscuss" />
|
||||
</el-col>
|
||||
<template v-if="isDiscussFinished">
|
||||
<el-col :span="24" v-for="i in discussList" :key="i.id">
|
||||
@@ -49,11 +38,7 @@
|
||||
<div class="carousel-font" :style="{ color: item.color }">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<el-image
|
||||
style="width: 100%; height: 100%"
|
||||
:src="item.logo"
|
||||
fit="cover"
|
||||
/>
|
||||
<el-image style="width: 100%; height: 100%" :src="item.logo" fit="cover" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</el-col>
|
||||
@@ -101,20 +86,22 @@
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<InfoCard header="访问统计" class="VisitsLineChart" text="详情">
|
||||
<InfoCard header="访问统计" class="VisitsLineChart" text="(New)全站历史统计" @onClickText="onClickAccessLog">
|
||||
<template #content>
|
||||
<VisitsLineChart :option="statisOptions" class="statisChart" />
|
||||
</template>
|
||||
</InfoCard>
|
||||
|
||||
<el-dialog v-model="accessLogDialogVisible" title="全站历史统计" width="1200px" center>
|
||||
<AccessLogChart :option="accessLogOptins" class="accessLogChart" />
|
||||
</el-dialog>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<InfoCard header="简介" text="详情">
|
||||
<InfoCard header="简介" text="">
|
||||
<template #content>
|
||||
<div class="introduce">
|
||||
没有什么能够阻挡,人类对代码<span style="color: #1890ff"
|
||||
>优雅</span
|
||||
>的追求
|
||||
没有什么能够阻挡,人类对代码<span style="color: #1890ff">优雅</span>的追求
|
||||
</div>
|
||||
</template>
|
||||
</InfoCard>
|
||||
@@ -122,12 +109,7 @@
|
||||
|
||||
<el-col :span="24">
|
||||
<template v-if="isPointFinished">
|
||||
<InfoCard
|
||||
:items="pointList"
|
||||
header="财富排行榜"
|
||||
text="关于钱钱"
|
||||
height="400"
|
||||
>
|
||||
<InfoCard :items="pointList" header="财富排行榜" text="关于钱钱" height="400">
|
||||
<template #item="temp">
|
||||
<PointsRanking :pointsData="temp" />
|
||||
</template>
|
||||
@@ -135,19 +117,16 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<InfoCard header="本月排行" text="更多">
|
||||
<template #content> <Skeleton /></template>
|
||||
<template #content>
|
||||
<Skeleton />
|
||||
</template>
|
||||
</InfoCard>
|
||||
</template>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<template v-if="isFriendFinished">
|
||||
<InfoCard
|
||||
:items="friendList"
|
||||
header="推荐好友"
|
||||
text="更多"
|
||||
height="400"
|
||||
>
|
||||
<InfoCard :items="friendList" header="推荐好友" text="更多" height="400">
|
||||
<template #item="temp">
|
||||
<RecommendFriend :friendData="temp" />
|
||||
</template>
|
||||
@@ -155,18 +134,15 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<InfoCard header="推荐好友" text="更多">
|
||||
<template #content> <Skeleton /></template>
|
||||
<template #content>
|
||||
<Skeleton />
|
||||
</template>
|
||||
</InfoCard>
|
||||
</template>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<template v-if="isThemeFinished">
|
||||
<InfoCard
|
||||
:items="themeList"
|
||||
header="推荐主题"
|
||||
text="更多"
|
||||
height="400"
|
||||
>
|
||||
<InfoCard :items="themeList" header="推荐主题" text="更多" height="400">
|
||||
<template #item="temp">
|
||||
<ThemeData :themeData="temp" />
|
||||
</template>
|
||||
@@ -174,7 +150,9 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<InfoCard header="推荐主题" text="更多">
|
||||
<template #content> <Skeleton /></template>
|
||||
<template #content>
|
||||
<Skeleton />
|
||||
</template>
|
||||
</InfoCard>
|
||||
</template>
|
||||
</el-col>
|
||||
@@ -197,7 +175,8 @@ import PlateCard from "@/components/PlateCard.vue";
|
||||
import ScrollbarInfo from "@/components/ScrollbarInfo.vue";
|
||||
import BottomInfo from "@/components/BottomInfo.vue";
|
||||
import VisitsLineChart from "./components/VisitsLineChart/index.vue";
|
||||
import { access } from "@/apis/accessApi.js";
|
||||
import AccessLogChart from "./components/AccessLogChart/Index.vue"
|
||||
import { access, getAccessList } from "@/apis/accessApi.js";
|
||||
import { getList } from "@/apis/plateApi.js";
|
||||
import { getList as bannerGetList } from "@/apis/bannerApi.js";
|
||||
import { getHomeDiscuss } from "@/apis/discussApi.js";
|
||||
@@ -215,10 +194,10 @@ import ThemeData from "./components/RecommendTheme/index.vue";
|
||||
import Skeleton from "@/components/Skeleton/index.vue";
|
||||
import useSocketStore from "@/stores/socket";
|
||||
|
||||
|
||||
const accessLogDialogVisible = ref(false)
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
const accessAllList = ref([]);
|
||||
|
||||
const plateList = ref([]);
|
||||
const discussList = ref([]);
|
||||
@@ -294,6 +273,25 @@ const statisOptions = computed(() => {
|
||||
},
|
||||
};
|
||||
});
|
||||
//历史全部访问统计
|
||||
const accessLogOptins = computed(() => {
|
||||
return {
|
||||
xAxis: {
|
||||
data: accessAllList.value?.map((item,index)=>{
|
||||
return item.creationTime.slice(0, 10);
|
||||
|
||||
})
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: accessAllList.value?.map((item,index)=>{
|
||||
return item.number;
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const handleToSign = () => {
|
||||
router.push("/activity/sign");
|
||||
@@ -309,12 +307,19 @@ watch(
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const onClickAccessLog = async () => {
|
||||
accessLogDialogVisible.value = true;
|
||||
const {data} = await getAccessList();
|
||||
|
||||
accessAllList.value = data;
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.home-box {
|
||||
width: 1300px;
|
||||
height: 100%;
|
||||
|
||||
.introduce {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: small;
|
||||
@@ -350,6 +355,7 @@ watch(
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.analyse {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -357,6 +363,7 @@ watch(
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.item {
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
@@ -368,6 +375,7 @@ watch(
|
||||
border: 1px solid #409eff;
|
||||
border-radius: 5px;
|
||||
color: #409eff;
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -385,6 +393,7 @@ watch(
|
||||
.name {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
@@ -407,11 +416,13 @@ watch(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.signIn {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #8a919f;
|
||||
|
||||
&-btn {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
@@ -426,7 +437,7 @@ watch(
|
||||
}
|
||||
}
|
||||
|
||||
.VisitsLineChart >>> .el-card__body {
|
||||
.VisitsLineChart>>>.el-card__body {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -434,5 +445,10 @@ watch(
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.accessLogChart {
|
||||
width: 1100px;
|
||||
height: 500px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="v-chart" ref="statis"></div>
|
||||
</template>
|
||||
|
||||
<script setup name="AccessLogChart">
|
||||
import { ref, defineEmits, defineProps, defineExpose } from "vue";
|
||||
import useEcharts from "@/hooks/useEcharts";
|
||||
import { accessLogEchartsConfig } from "../../hooks/accessLogEchartsConfig";
|
||||
const props = defineProps({
|
||||
option: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
const emits = defineEmits([
|
||||
"chart-click", // 点击chart
|
||||
]);
|
||||
|
||||
let statis = ref(null);
|
||||
const { resize } = useEcharts(statis, emits, props, accessLogEchartsConfig);
|
||||
defineExpose({
|
||||
resize,
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.v-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
18
Yi.Bbs.Vue3/src/views/home/hooks/accessLogEchartsConfig.js
Normal file
18
Yi.Bbs.Vue3/src/views/home/hooks/accessLogEchartsConfig.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export const accessLogEchartsConfig = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: []
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [],
|
||||
type: 'line',
|
||||
areaStyle: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -79,8 +79,22 @@ const startCallback = () => {
|
||||
// 假设后端返回的中奖索引是0
|
||||
// 调用stop停止旋转并传递中奖索引
|
||||
}, 3000)
|
||||
|
||||
try
|
||||
{
|
||||
const ddd=(await luckyWheel()).data;
|
||||
console.log(ddd,"dd");
|
||||
index= (await luckyWheel()).data;
|
||||
myLucky.value.stop(index)
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
finally{
|
||||
|
||||
myLucky.value.stop(index)
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
大家好呀~ 最近我这里收到很多人的疑惑
|
||||
- 从零开始我怎么手动分层依赖搭建一个项目
|
||||
- 如果想依赖其他组件应该怎么引入
|
||||
- 为什么要这样分层?和三层架构有什么区别?
|
||||
- 等等
|
||||
|
||||
这些问题,很多初学者会疑惑的地方,就是照葫芦画苗,但是也说不出理由来,最后反而失去了这样分层的优势。
|
||||
|
||||
每一层都是有它的理由,明确了意义,在编码阶段才更得心应手,发挥出这样分层的作用。
|
||||
|
||||
> 题外话,很是有缘,在完全没有接触过DDD的分层的时候,还是停留在三层架构的时期,对于大型复杂的依赖关系,Service满天飞,根本难以下手,写出的代码交错复杂,稍微隔一段时间没去回顾,再去看,就会被自己的代码给绕死,所以我就一直想换一种分层模式,想让各个服务结构更加清晰,不再是直肠子式的三层架构,那我做的第一件事情,就是把关系比较紧密的给装在一起当成一个整体,各个整体之间不轻易去依赖,而是通过事件去抛出去。这就在早期形成了一套依赖关系比较清晰的框架,经过不断的项目历练,慢慢的就变成了业务聚合、应用组装等等概念,等到机会接触到了DDD分层一看,`哇塞,怎么那么像?这怕不是他抄我的分层嘛`,当然开个玩笑,从此对这块的了解更加加深,确实值得很多大家应该学习的地方,提升一下自己。
|
||||
|
||||
> 一千个码农,就有一千个DDD。DDD不一定完全是对的,但一定是值得学习使用的
|
||||
|
||||
> 在此,老橙子很乐意以我的血淋淋的教训的角度给大家讲清楚,框架的分层依赖关系,同时也是标准的`DDD分层架构`依赖关系,希望能给大家带来一些帮助~
|
||||
|
||||
## 依赖关系
|
||||
这个标题其实已经在之前一个章节有涉及到一部分,`目录结构`,不过既然想弄清楚依赖关系,就得先了解目录结构,大家可以先回顾一下,弄清楚有哪些文件,再看它的依赖关系
|
||||
|
||||

|
||||
|
||||
从上至下,分别是
|
||||
|
||||
- 应用层
|
||||
- 应用抽象层
|
||||
- 领域层
|
||||
- 领域共享层
|
||||
- 基础设施ORM层
|
||||
- Host主机
|
||||
|
||||
对应的项目引用关系下图所示:
|
||||

|
||||
|
||||
可以从上图很直观的看出:
|
||||
|
||||
最顶层的`host主机与下面的应用`没有关系,意味着host主机可以是web、可以是cs、可以等等,所以我们的host主机只充当一个web启动的作用,不包含业务逻辑
|
||||
|
||||
另外,发现目录中只包含ORM,好像没有基础设施层,其实是一个包含关系,是ORM属于基础设施中的一种,但是为了将orm抽象,特意把ORM独立了一层
|
||||
|
||||
由于框架的基础设施都已经在Framework里面包含,所以没有单独建立一层新的基础设施层,你完全可以`再加一层`在ORM下当作基础设施即可。
|
||||
|
||||
> web应用中,对数据库操作涉及非常广,所以ABP将ORM特意抽象出来,可实现EFCore、MongoDb等数据库无缝切换。
|
||||
|
||||
## 每层的内容
|
||||
- **基础设施层**,存放各种通用方法、ORM等
|
||||
- **领域共享层**,存放枚举、常量、事件参数、对应用层的dto
|
||||
- **领域层**,存放业务的内部实现(领域服务)、领域事件。对外也就是对应用层可以暴露实体、对应用层的dto
|
||||
- **应用抽象层**,存放应用层抽象、系统对外的dto
|
||||
- **应用层**,存放对业务的组装(领域服务),对外暴露的dto,主要是对外的用户交互相关的业务
|
||||
- **Host主机**,存放跟WebHost,类似web启动器,包括中间件、swagger、等与web相关
|
||||
|
||||
## 为什么要这样分层?
|
||||
弄清楚了分层结构,已经依赖关系,我们可以探究里面的原理,我们为什么要这样做?这样做的道理或者好处是什么?
|
||||
|
||||
### 基础设施层
|
||||
首先,不管是任何系统,想要对通用方法的复用,就必然有基础设施供整个系统的使用。这个`基础设施层`其实就类似于三层架构的`Common层`,这个没啥说的
|
||||
|
||||
这一层目的是为了通用方法的复用
|
||||
|
||||
### 领域层
|
||||
领域,这个词经常出现,感觉很抽象,我们可以把他类比成`盒子`或者`模块`,比如电商系统的售前、售后,我们这可以分成两个大的`领域`。
|
||||
这里是整个结构的业务实现在这块,也是最重的一层。严格来说,不同领域之间想要调用,比如售后领域想查看售前领域的记录,可以通过事件抛出来,从而各个领域没有之间强耦合。
|
||||
|
||||
这一层的作用是实现各个领域的业务
|
||||
|
||||
### 领域共享层
|
||||
这里只是存放一些领域所要使用的枚举、常量等,除了基础设施不要有其他的依赖,轻、也轻越好,因为他的目的不是对内,而是提供给给外部系统使用的。
|
||||
|
||||
这一层的作用是存放共享的枚举、常量等,也是为了存放领域层暴露给应用层的dto(比如领域层想暴露一个用户角色菜单对象,实体无法满足,就需可以创建这样的一个dto了,在其他文章可能大家会叫做info对象)
|
||||
|
||||
### 应用层
|
||||
领域层是对内的,外部系统是不清楚的,那我们的业务实现想暴露出去,要通过应用层进行包装,因为应用层是对外的,对用户交互相关处理,比如api、signalr等
|
||||
|
||||
这一层的目的是对领域层的组装及数据的效验、对外用户交互相关处理。
|
||||
|
||||
### 应用抽象层
|
||||
这里主要目的是将应用层暴露进行抽象,该层是为了提供外部系统之间依赖,于`领域共享层`类似,所以这一层也非常的轻,从上面的依赖关系图来看,`领域共享层`于`应用抽象层`是一条线性依赖。
|
||||
|
||||
这一层目的是为了`应用抽象层`可以被外部系统之间引用,从而通过Http代理等技术,实现远程调用接口访问`应用层`进行交互。这里感兴趣的可以看一下Abp.VNext的HttpProxy模块,默认意框架中不引入,等有需要的时候可以使用。
|
||||
|
||||
|
||||
## 小结
|
||||
以上分层,是DDD推荐的一种最佳实践方式之一,很荣幸,通过我十几个上线项目应用情况,事实证明,非常nice
|
||||
|
||||
补充一句话,我不是DDD的崇拜者,只是在被各种业务折磨的不行的码农想追求一种方法,让花更少的时间更好的去完成任务。
|
||||
|
||||
所以,我的内容是比较偏向实践,正如意框架一般,一个追求以用户体验为中心的框架。
|
||||
|
||||
意框架很多地方并没有完全严格走DDD,包括Abp等有名的框架,过于严格,代码体验感反而是下降。有很多场景其实是没有必要的,过长的代码、过绕的领域关系,写个增删改查还得花很多时间,这也是为什么DDD难以被接受的原因之一。
|
||||
BIN
Yi.Doc.Md/01.框架快速开始教程/00.快速上手/image-2.png
Normal file
BIN
Yi.Doc.Md/01.框架快速开始教程/00.快速上手/image-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
BIN
Yi.Doc.Md/01.框架快速开始教程/00.快速上手/image-3.png
Normal file
BIN
Yi.Doc.Md/01.框架快速开始教程/00.快速上手/image-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
Reference in New Issue
Block a user