init
This commit is contained in:
21
src/components/SvgIcon.vue
Normal file
21
src/components/SvgIcon.vue
Normal 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>
|
||||
36
src/components/addressCard.vue
Normal file
36
src/components/addressCard.vue
Normal 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>
|
||||
226
src/components/chartCard.vue
Normal file
226
src/components/chartCard.vue
Normal 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
98
src/components/editor.vue
Normal 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>
|
||||
99
src/components/totalEarnings.vue
Normal file
99
src/components/totalEarnings.vue
Normal 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>
|
||||
125
src/components/verificationCode.vue
Normal file
125
src/components/verificationCode.vue
Normal 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>
|
||||
Reference in New Issue
Block a user