This commit is contained in:
2023-09-13 18:29:35 +08:00
commit 4ac8391a9a
126 changed files with 15555 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
<template>
<el-icon v-if="name" :size="size" :color="color">
<component :is="name"></component>
</el-icon>
</template>
<script setup>
const props = defineProps({
name: {
type: String,
required: true,
default: "",
},
size: {
type: [String, Number],
},
color: {
type: String,
},
});
</script>

View File

@@ -0,0 +1,36 @@
<template>
<el-cascader :placeholder="placeholder" :options="regionData" v-model="selectedOptions" @change="change"></el-cascader>
</template>
<script setup>
import { regionData, codeToText } from 'element-china-area-data'
import { defineEmits, defineExpose } from 'vue'
const emit = defineEmits(['change'])
const placeholder = ref('')
const selectedOptions = ref([])
// 选择区域
function change(e) {
emit('change', [{
code: selectedOptions.value[0],
label: codeToText[selectedOptions.value[0]]
}, {
code: selectedOptions.value[1],
label: codeToText[selectedOptions.value[1]]
}, {
code: selectedOptions.value[2],
label: codeToText[selectedOptions.value[2]]
}])
}
// 设置默认值
function setValue(arr) {
selectedOptions.value = arr
}
defineExpose({
placeholder,
setValue
})
</script>

View File

@@ -0,0 +1,226 @@
<template>
<div class="chart_header">
<div class="item" :class="{ active1: chartType == 1 }" @click="chartTypeChange(1)">
<div class="icon"></div>
<span>收益</span>
</div>
<div class="item" :class="{ active2: chartType == 2 }" @click="chartTypeChange(2)">
<div class="icon"></div>
<span>流水</span>
</div>
</div>
<div class="chart_wrap" v-loading="chartLoading">
<div ref="lineRef1" class="item" style="height: 600px;"></div>
<div ref="lineRef2" class="item" style="height: 600px;"></div>
</div>
</template>
<script setup>
import { getChartData } from '@/api/home.js'
import * as echarts from "echarts"
import { dayjs } from 'element-plus'
import hooks from '@/hooks'
// 定义变量内容
const lineRef1 = ref();
const lineRef2 = ref();
const chartsObj = {
lineRef1: '',
lineRef2: '',
myCharts: [],
};
const chartLoading = ref(true)
const chartType = ref(1)
const chartYear = ref(dayjs().format('YYYY'))
const props = defineProps({
userId: {
type: [String, Number],
default: hooks.useLocalStorage.get('userInfo').userId
}
})
// 初始化折线图
function initDeicount(yearData = [], sevenData = [], sevenDataTime = []) {
if (!chartsObj.lineRef1 && !chartsObj.lineRef2) {
chartsObj.lineRef1 = echarts.init(lineRef1.value);
chartsObj.lineRef2 = echarts.init(lineRef2.value);
}
const option1 = {
title: {
text: chartType.value == 1 ? '年度收益' : '年度流水',
x: 'center',
y: '95%'
},
tooltip: {
trigger: 'axis'
},
xAxis: [
{
type: 'category',
data: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二'],
axisPointer: {
type: 'shadow'
},
axisLabel: {
interval: 0
}
}
],
yAxis: {
type: "value"
},
series: [
{
type: 'bar',
data: yearData,
barWidth: '20',
itemStyle: {
color: chartType.value == 1 ? '#5470C6' : '#91CB75'
}
}
]
};
const option2 = {
title: {
text: chartType.value == 1 ? '七日收益' : '七日流水',
x: 'center',
y: '95%'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: "category",
data: sevenDataTime
},
yAxis: {
type: "value",
},
series: [
{
type: "line",
data: sevenData,
itemStyle: {
color: chartType.value == 1 ? '#5470C6' : '#91CB75'
}
}
]
};
chartsObj.lineRef1.setOption(option1)
chartsObj.lineRef2.setOption(option2)
chartsObj.myCharts.push(chartsObj.lineRef1)
chartsObj.myCharts.push(chartsObj.lineRef2)
}
// 改变chart类型
function chartTypeChange(t) {
chartType.value = t
chartLoading.value = true
getChartDataMethod()
}
// 获取chart数据
async function getChartDataMethod() {
try {
let yearData = ''
let sevenData = ''
let sevenDataTime = ''
if (chartType.value == 1) {
// 收益
const res1 = await getChartData(4, props.userId)
const res2 = await getChartData(2, props.userId)
yearData = res1.map(item => item.price)
sevenData = res2.map(item => item.price)
sevenDataTime = res2.map(item => dayjs(item.times).format('MM/DD'))
} else {
// 流水
const res1 = await getChartData(3, props.userId)
const res2 = await getChartData(1, props.userId)
yearData = res1.map(item => item.consumeFee)
sevenData = res2.map(item => item.consumeFee)
sevenDataTime = res2.map(item => dayjs(item.times).format('MM/DD'))
}
initDeicount(yearData, sevenData, sevenDataTime)
chartLoading.value = false
} catch (error) { }
}
// 批量设置 echarts resize
function initEChartsResize() {
var myEvent = new Event("resize");
window.addEventListener("resize", () => {
nextTick(() => {
for (let i = 0; i < chartsObj.myCharts.length; i++) {
setTimeout(() => {
chartsObj.myCharts[i].resize();
}, 1000);
}
});
});
window.dispatchEvent(myEvent);
}
onMounted(() => {
getChartDataMethod()
})
onActivated(() => {
initEChartsResize();
});
</script>
<style scoped lang="scss">
.chart_header {
display: flex;
justify-content: center;
padding-top: 20px;
.item {
display: flex;
align-items: center;
&:last-child {
margin-left: 20px;
}
&:hover {
cursor: pointer;
}
&.active1 {
.icon {
background-color: #5470C6;
}
}
&.active2 {
.icon {
background-color: #91CB75;
}
}
.icon {
width: 40px;
height: 20px;
border-radius: 2px;
background-color: #ccc;
}
span {
font-size: 14px;
color: #999;
margin-left: 8px;
}
}
}
.chart_wrap {
display: flex;
padding-bottom: 20px;
.item {
flex: 1;
}
}
</style>

98
src/components/editor.vue Normal file
View File

@@ -0,0 +1,98 @@
<template>
<div class="editor-container">
<Toolbar :editor="editorRef" :mode="mode" />
<Editor :mode="mode" :defaultConfig="state.editorConfig" :style="{ height }" v-model="state.editorVal" @onCreated="handleCreated" @onChange="handleChange" />
</div>
</template>
<script setup>
// https://www.wangeditor.com/v5/for-frame.html#vue3
import "@wangeditor/editor/dist/css/style.css";
import { Toolbar, Editor } from "@wangeditor/editor-for-vue";
// 定义父组件传过来的值
const props = defineProps({
// 是否禁用
disable: {
type: Boolean,
default: () => false,
},
// 内容框默认 placeholder
placeholder: {
type: String,
default: () => "请输入内容...",
},
// 模式,可选 <default|simple>,默认 default
mode: {
type: String,
default: () => "default",
},
// 高度
height: {
type: String,
default: () => "300px",
},
// 双向绑定,用于获取 editor.getHtml()
getHtml: String,
// 双向绑定,用于获取 editor.getText()
getText: String,
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(["update:getHtml", "update:getText"]);
// 定义变量内容
const editorRef = shallowRef();
const state = reactive({
editorConfig: {
placeholder: props.placeholder,
},
editorVal: props.getHtml,
});
// 编辑器回调函数
const handleCreated = (editor) => {
editorRef.value = editor; // 记录 editor 实例,重要!
};
// 编辑器内容改变时
const handleChange = (editor) => {
emit("update:getHtml", editor.getHtml());
emit("update:getText", editor.getText());
};
nextTick(() => {
// 监听是否禁用改变
watch(
() => props.disable,
(bool) => {
const editor = editorRef.value;
if (editor == null) return;
bool ? editor.disable() : editor.enable();
},
{ immediate: true }
);
});
// 监听双向绑定值改变,用于回显
watch(
() => props.getHtml,
(val) => {
state.editorVal = val;
}
);
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,99 @@
<template>
<el-dialog v-model="showDialog" title="累计收益" width="80%">
<el-space>
<el-input placeholder="请输入订单号搜索" v-model="tableOptions.orderNumber" style="width: 200px;" />
<el-input placeholder="请输入商户号搜索" v-model="tableOptions.merchantCode" style="width: 200px;" />
<el-button type="primary" icon="Search" @click="searchHandle">搜索</el-button>
</el-space>
<div class="mt15">
<el-table :data="tableOptions.list" v-loading="tableOptions.loading">
<el-table-column prop="orderNumber" label="订单号"></el-table-column>
<el-table-column prop="merchantCode" label="商户号"></el-table-column>
<el-table-column prop="price" label="收益额"></el-table-column>
<el-table-column prop="current_fee" label="推广费率">
<template #default="scope">
<el-text type="primary">{{ scope.row.currentFee }}%</el-text>
</template>
</el-table-column>
<el-table-column prop="loginName" label="名称">
<template #default="scope">
<el-text>{{ scope.row.loginName }}</el-text>
<el-tag disable-transitions style="margin-left: 8px;">{{ typeNames[scope.row.typeCode] }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="merchantName" label="商户名称">
</el-table-column>
<el-table-column prop="createDt" label="时间">
<template #default="scope">
{{ dayjs(scope.row.createDt).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
</el-table>
</div>
<div class="mt15">
<el-pagination layout="prev, pager, next, total, sizes, jumper" v-model:current-page="tableOptions.pageNum"
v-model:page-size="tableOptions.pageSzie" :page-size="tableOptions.pageSzie" :page-sizes="[10, 20, 30, 50]"
:total="tableOptions.total" @size-change="paginationChange" @current-change="paginationChange" />
</div>
</el-dialog>
</template>
<script setup>
import { dayjs } from 'element-plus';
import { typeNames } from '@/utils/index.js'
import { queryProfit } from '@/api/organization.js'
const showDialog = ref(false)
// 表格参数
const tableOptions = reactive({
loading: true,
reqUserId: '', // 上级id
orderNumber: '', // 订单号
merchantCode: '', // 商户号
list: [],
total: 0,
pageNum: 1,
pageSzie: 10
})
// 搜索
function searchHandle() {
tableOptions.pageNum = 1;
paginationChange()
}
// 分页回调
function paginationChange() {
tableOptions.loading = true
getTableDate()
}
// 获取机构列表
async function getTableDate() {
try {
const res = await queryProfit({
reqUserId: tableOptions.reqUserId,
orderNumber: tableOptions.orderNumber,
merchantCode: tableOptions.merchantCode,
pageNum: tableOptions.pageNum,
pageSzie: tableOptions.pageSzie
})
tableOptions.loading = false
tableOptions.list = res.list
tableOptions.total = res.total
} catch (error) { }
}
const show = (reqUserId) => {
tableOptions.reqUserId = reqUserId
showDialog.value = true
getTableDate()
}
defineExpose({
show
})
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,125 @@
<template>
<canvas id="canvas" :width="props.width" :height="props.height" @click="handleCanvas"> </canvas>
</template>
<script setup>
const props = defineProps({
width: {
type: Number,
default: 100,
},
height: {
type: Number,
default: 38,
},
quantity: {
type: Number,
default: 4,
},
line: {
type: Number,
default: 10,
},
spot: {
type: Number,
default: 50,
},
});
let trueCode = ref(""); //保存正确的验证码
// 只用于传参,并且数组长度不能「多于」下面验证码遍历的次数,不然最终得到的 trueCode 会有问题
let verificationCode = [];
function draw(showCode) {
var canvas_width = document.querySelector("#canvas").clientWidth;
var canvas_height = document.querySelector("#canvas").clientHeight;
var canvas = document.getElementById("canvas"); // 获取到canvas
var context = canvas.getContext("2d"); // 获取到canvas画图
canvas.width = canvas_width;
canvas.height = canvas_height;
var sCode = "a,b,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,W,X,Y,Z,1,2,3,4,5,6,7,8,9,0";
var aCode = sCode.split(",");
var aLength = aCode.length; // 获取到数组的长度
// 验证码个数
for (var i = 0; i < props.quantity; i++) {
var j = Math.floor(Math.random() * aLength); // 获取到随机的索引值
var deg = (Math.random() * 30 * Math.PI) / 180; // 产生0~30之间的随机弧度
var txt = aCode[j]; // 得到随机的一个内容
showCode[i] = txt.toLowerCase(); // 依次把取得的内容放到数组里面
var x = 10 + i * 20; // 文字在canvas上的x坐标
var y = 20 + Math.random() * 8; // 文字在canvas上的y坐标
context.font = "bold 23px 微软雅黑";
context.translate(x, y);
context.rotate(deg);
context.fillStyle = randomColor();
context.fillText(txt, 0, 0);
context.rotate(-deg);
context.translate(-x, -y);
}
// 验证码上显示的线条
for (var i = 0; i < props.line; i++) {
context.strokeStyle = randomColor();
context.beginPath();
context.moveTo(Math.random() * canvas_width, Math.random() * canvas_height);
context.lineTo(Math.random() * canvas_width, Math.random() * canvas_height);
context.stroke();
}
// 验证码上显示的小点
for (var i = 0; i < props.spot; i++) {
context.strokeStyle = randomColor();
context.beginPath();
var x = Math.random() * canvas_width;
var y = Math.random() * canvas_height;
context.moveTo(x, y);
context.lineTo(x + 1, y + 1);
context.stroke();
}
// 最后把取得的验证码数组存起来,方式不唯一
var num = showCode.join("");
trueCode.value = num;
}
// 得到随机的颜色值
function randomColor() {
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
return "rgb(" + r + "," + g + "," + b + ")";
}
const emits = defineEmits(["getCode"]);
// canvas 点击刷新
function handleCanvas() {
draw(verificationCode);
emits("getCode", trueCode.value);
}
onMounted(() => {
draw(verificationCode);
emits("getCode", trueCode.value);
});
</script>
<style lang="scss" scoped>
#canvas {
margin-right: 1%;
display: block;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
margin-right: 80px;
}
.input-val {
width: 50%;
background: #ffffff;
height: 2.8rem;
border-radius: 5px;
border: none;
padding: 0 0 0 12px;
border: 1px solid rgba(0, 0, 0, 0.2);
}
</style>