feat: 初始化
This commit is contained in:
27
pages/partner/rebateSetting/index.scss
Normal file
27
pages/partner/rebateSetting/index.scss
Normal file
@ -0,0 +1,27 @@
|
||||
.container {
|
||||
padding: 0 16px 24px;
|
||||
.swiper {
|
||||
margin: 14px 0 18px;
|
||||
}
|
||||
.title {
|
||||
margin: 14px 0 15px;
|
||||
}
|
||||
}
|
||||
.tableSection {
|
||||
margin-top: 10px;
|
||||
.tableTh {
|
||||
.columnItem {
|
||||
border-top: 2px solid #29bbe4;
|
||||
}
|
||||
}
|
||||
.columnItem:first-child {
|
||||
background-color: #fff;
|
||||
position: sticky;
|
||||
left: 0px;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0px 5px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.uni-table-tr:hover .columnItem:first-child {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
}
|
65
pages/partner/rebateSetting/index.vue
Normal file
65
pages/partner/rebateSetting/index.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<NavBar />
|
||||
<view class="container">
|
||||
<view class="title">
|
||||
<PageTitle :title="$t('ib.fundSetting')" />
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="tableSection">
|
||||
<uni-table :emptyText="$t('common.noData')" v-if="!tableLoading">
|
||||
<uni-tr class="tableTh">
|
||||
<uni-th align="left" class="columnItem">{{ $t('fundSetting.rebateRule') }}</uni-th>
|
||||
</uni-tr>
|
||||
<uni-tr v-for="row in rebateRuleList" :key="row.rowKey ?? row.id" @click="handleRowClick(row)" >
|
||||
<uni-td class="columnItem">
|
||||
<view style="width: 100%;display: flex;justify-content: space-between;">
|
||||
<view>{{ row?.['rebate_name'] ?? '-' }}</view>
|
||||
<view><uni-icons type="right" size="40upx"></uni-icons></view>
|
||||
</view>
|
||||
</uni-td>
|
||||
</uni-tr>
|
||||
</uni-table>
|
||||
<Spin v-show="tableLoading" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { queryRebate } from '@/services/partner/myClient.ts';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tableLoading: false,
|
||||
rebateRuleList: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
async loadData() {
|
||||
this.tableLoading = true
|
||||
const resp = await queryRebate()
|
||||
this.tableLoading = false
|
||||
this.rebateRuleList = resp.data
|
||||
},
|
||||
handleRowClick(currentRow) {
|
||||
const userStore = useUserStore();
|
||||
userStore.setCurrentRow(currentRow)
|
||||
uni.navigateTo({
|
||||
url: "/pages/partner/rebateSetting/tree"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
109
pages/partner/rebateSetting/tree.scss
Normal file
109
pages/partner/rebateSetting/tree.scss
Normal file
@ -0,0 +1,109 @@
|
||||
.container {
|
||||
padding: 0 16px 24px;
|
||||
.swiper {
|
||||
margin: 14px 0 18px;
|
||||
}
|
||||
.title {
|
||||
margin: 14px 0 15px;
|
||||
}
|
||||
.searchWrapper {
|
||||
display: flex;
|
||||
column-gap: 8px;
|
||||
align-items: center;
|
||||
.searchBtn {
|
||||
width: 92px;
|
||||
height: 34px;
|
||||
flex: 0 1 92px;
|
||||
}
|
||||
}
|
||||
.infoWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 11px;
|
||||
margin: 17px 0 26px;
|
||||
.ib,
|
||||
.client {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
column-gap: 4px;
|
||||
padding: 4px 8px;
|
||||
box-sizing: border-box;
|
||||
min-width: 112px;
|
||||
height: 32px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
.info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.value {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ib {
|
||||
background-color: #0f3675;
|
||||
}
|
||||
.client {
|
||||
background-color: #29bbe4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tableSection {
|
||||
max-height: 60vh;
|
||||
margin-top: 10px;
|
||||
overflow: auto;
|
||||
.tableTh {
|
||||
.columnItem {
|
||||
border-top: 2px solid #29bbe4;
|
||||
}
|
||||
}
|
||||
.columnItem:first-child {
|
||||
background-color: #fff;
|
||||
position: sticky;
|
||||
left: 0px;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0px 5px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.uni-table-tr:hover .columnItem:first-child {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-icon {
|
||||
width: 40upx;
|
||||
height: 40upx;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
margin: 4upx 0;
|
||||
height: 60upx;
|
||||
font-size: 24upx;
|
||||
// margin-left: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.level {
|
||||
flex-shrink: 0;
|
||||
height: 40upx;
|
||||
width: 40upx;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 40upx;
|
||||
font-size: 24upx;
|
||||
color: #fff;
|
||||
background-color: #29bbe4;
|
||||
margin: 0 16upx;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
height: 60upx;
|
||||
line-height: 60upx;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
397
pages/partner/rebateSetting/tree.vue
Normal file
397
pages/partner/rebateSetting/tree.vue
Normal file
@ -0,0 +1,397 @@
|
||||
<template>
|
||||
<NavBar />
|
||||
<view class="container">
|
||||
<view class="title">
|
||||
<PageTitle :title="$t('ib.fundSetting')" />
|
||||
</view>
|
||||
<template v-if="!loading && allJunior">
|
||||
<view style="height: 56upx; margin-bottom: 20upx" v-if="allJunior && allJunior.power == 'children'">
|
||||
{{ $t('fundSetting.childrenMaxHierarchy') }}
|
||||
<text style="font-size: 36upx">{{ selectedRow.hierarchy_value }}</text>
|
||||
</view>
|
||||
<view style="height: 80upx; margin-bottom: 20upx; position: relative" v-if="allJunior && allJunior.power == 'all'">
|
||||
<view class="searchWrapper">
|
||||
<uni-data-select :clear="false" :localdata="searchTypeOptions" v-model="searchType"></uni-data-select>
|
||||
<uni-easyinput primaryColor="#29BBE4" v-model="searchValue" />
|
||||
<button class="primaryButton searchBtn" @click="query">{{ $t('form.search') }}</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<uni-popup ref="resultDialog" :isMaskClick="false">
|
||||
<view style="width: 600upx">
|
||||
<view class="tableSection">
|
||||
<uni-table :emptyText="$t('common.noData')" @click="hideSearchDialog">
|
||||
<uni-tr v-for="row in searchResult" :key="row.rowKey ?? row.id" @click="handleRow(row)">
|
||||
<uni-td class="columnItem">
|
||||
<view style="width: 100%">
|
||||
<view>{{ row?.['text'] ?? '-' }}</view>
|
||||
</view>
|
||||
</uni-td>
|
||||
</uni-tr>
|
||||
</uni-table>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
<view>
|
||||
<template v-if="!loading">
|
||||
<view v-if="allJunior" style="width: 100%; padding: 0 20upx; box-sizing: border-box">
|
||||
<scroll-view :style="{ height: contentHeight }" scroll-y scroll-x scroll-with-animation="true" @scroll="handleScroll" :scroll-top="scrollTop">
|
||||
<BaseTree ref="tree" class="mtl-tree" treeLine v-model="treeNodes">
|
||||
<template #default="{ node, stat }">
|
||||
<image
|
||||
src="@/static/partner/collapse.png"
|
||||
mode="aspectFill"
|
||||
class="tree-node-icon"
|
||||
v-if="stat.open && stat.children.length"
|
||||
@click.native="stat.open = false"
|
||||
></image>
|
||||
<image
|
||||
src="@/static/partner/unfold.png"
|
||||
mode="aspectFill"
|
||||
class="tree-node-icon"
|
||||
v-else-if="!stat.open && stat.children.length"
|
||||
@click.native="stat.open = true"
|
||||
></image>
|
||||
<view class="tree-node" :id="'refN' + node.id">
|
||||
<text class="level">{{ stat.level }}</text>
|
||||
<text class="label">{{ node.text }}</text>
|
||||
<uni-easyinput
|
||||
type="digit"
|
||||
inputmode="decimal"
|
||||
:readonly="node.readonly"
|
||||
:disabled="node.readonly"
|
||||
:clearable="false"
|
||||
:primaryColor="(stat.level == 1 ? selectedRow.hierarchy_value : stat.parent.data.val) < node.val ? 'red' : '#29BBE4'"
|
||||
style="min-width: 100upx; margin-left: 10upx; flex-grow: 0"
|
||||
:class="{ 't-input-error': (stat.level == 1 ? selectedRow.hierarchy_value : stat.parent.data.val) < node.val && !node.readonly }"
|
||||
v-model="node.val"
|
||||
/>
|
||||
<text class="diff" :class="{ 'error-diff': (stat.parent.data.val ? stat.parent.data.val : 0) < node.val }" v-if="stat.level != 1">
|
||||
{{ $t('partner.difference', { n: (stat.parent.data.val ? stat.parent.data.val : 0) - (node.val ? node.val : 0) }) }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
</BaseTree>
|
||||
</scroll-view>
|
||||
<view>
|
||||
<!-- 保存和返回 -->
|
||||
<button class="primaryButton" style="margin-top: 20upx" :disabled="saveLoading" @click="save">
|
||||
<image v-show="saveLoading" src="/static/loadingCircle.svg" mode="aspectFit" style="width: 16px; height: 16px"></image>
|
||||
{{ $t('form.save') }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<Spin v-show="loading" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { getAllJunior, saveRebateSet } from '@/services/partner/myClient.ts';
|
||||
import BaseTree, { walkTreeData } from '@/pages/components/baseTree/baseTree.vue';
|
||||
import '@/pages/components/baseTree/style/default.css';
|
||||
import '@/pages/components/baseTree/style/material-design.css';
|
||||
|
||||
export default {
|
||||
components: { BaseTree },
|
||||
data() {
|
||||
return {
|
||||
scrollTop: 0,
|
||||
curScrollTop: 0,
|
||||
contentHeight: 'auto',
|
||||
loading: true,
|
||||
saveLoading: false,
|
||||
searchTypeOptions: [
|
||||
{ text: this.$t('fundSetting.account'), value: 'n' },
|
||||
{ text: this.$t('fundSetting.value'), value: 'v' }
|
||||
],
|
||||
searchType: 'n',
|
||||
searchValue: null,
|
||||
searchResult: null,
|
||||
allJunior: null,
|
||||
bigAgentId: null,
|
||||
selectedRow: null,
|
||||
treeNodes: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async loadData(currentRow) {
|
||||
this.loading = true;
|
||||
const resp = await getAllJunior({ rebate_id: currentRow.id });
|
||||
this.loading = false;
|
||||
if (resp.code == 0) {
|
||||
this.allJunior = resp.data;
|
||||
if (resp.data.power === 'all') {
|
||||
this.loadTree(currentRow);
|
||||
} else if (resp.data.power === 'children') {
|
||||
this.loadChildren(currentRow);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 加载所有下级
|
||||
*/
|
||||
loadTree(currentRow) {
|
||||
const { tree, listAll, listVal } = this.allJunior;
|
||||
let valueMap = new Map();
|
||||
if (listVal && listVal.length) {
|
||||
for (let i = 0; i < listVal.length; i++) {
|
||||
let val = listVal[i].hierarchy_value;
|
||||
let if_fixed = listVal[i].if_fixed;
|
||||
if (val == null || val == '' || isNaN(val)) {
|
||||
val = 0;
|
||||
}
|
||||
valueMap.set(listVal[i].user_id, { val: val, if_fixed: if_fixed });
|
||||
}
|
||||
}
|
||||
let node0 = [];
|
||||
if (tree && tree.length) {
|
||||
this.bigAgentId = listAll[0].id;
|
||||
node0.push({
|
||||
level: 0,
|
||||
levelNum: listAll[0].level,
|
||||
id: listAll[0].id,
|
||||
text: listAll[0].user_code + '(' + listAll[0].id + ')',
|
||||
// 大代理的值时默认的,不允许修改
|
||||
readonly: true,
|
||||
pid: 0,
|
||||
val: currentRow.hierarchy_value
|
||||
});
|
||||
}
|
||||
node0[0].children = this.getTree(tree, valueMap, 0);
|
||||
this.treeNodes = node0;
|
||||
valueMap.clear();
|
||||
},
|
||||
/**
|
||||
* 加载直属下级
|
||||
*/
|
||||
loadChildren(currentRow) {
|
||||
const { listAll, noChangeUserMap } = this.allJunior;
|
||||
let nodes = [];
|
||||
for (let i = 0; i < listAll.length; i++) {
|
||||
let readonly = false;
|
||||
if (noChangeUserMap != null && !this.isEmpty(noChangeUserMap[listAll[i].id])) {
|
||||
readonly = true;
|
||||
}
|
||||
let item = listAll[i];
|
||||
nodes.push({
|
||||
level: 0,
|
||||
readonly: readonly,
|
||||
id: item.id,
|
||||
text: item.user_code + '(' + item.id + ')',
|
||||
val: this.returnFloat(item.hierarchy_value),
|
||||
children: []
|
||||
});
|
||||
}
|
||||
this.treeNodes = nodes;
|
||||
},
|
||||
getTree(tree, valueMap, level) {
|
||||
if (!tree) {
|
||||
return [];
|
||||
}
|
||||
let nodes = [];
|
||||
const { noChangeUserMap } = this.allJunior;
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
let readonly = false;
|
||||
if (noChangeUserMap != null && !this.isEmpty(noChangeUserMap[tree[i].id])) {
|
||||
readonly = true;
|
||||
}
|
||||
let val = 0;
|
||||
let if_fixed = 0;
|
||||
if (valueMap && valueMap.get(tree[i].id)) {
|
||||
val = valueMap.get(tree[i].id).val;
|
||||
if_fixed = valueMap.get(tree[i].id).if_fixed;
|
||||
}
|
||||
nodes.push({
|
||||
readonly: readonly,
|
||||
levelNum: tree[i].level,
|
||||
id: tree[i].id,
|
||||
text: tree[i].user_code + '(' + tree[i].id + ')',
|
||||
val: val,
|
||||
if_fixed: if_fixed,
|
||||
level: level + 1,
|
||||
children: this.getTree(tree[i].child, valueMap, level + 1)
|
||||
});
|
||||
}
|
||||
return nodes;
|
||||
},
|
||||
getData(nodes, result, maxValue) {
|
||||
if (nodes && nodes.length) {
|
||||
for (var j = 0; j < nodes.length; j++) {
|
||||
let i = nodes[j];
|
||||
if (!i.readonly) {
|
||||
result.push({
|
||||
user_id: i.id,
|
||||
hierarchy_value: i.val
|
||||
});
|
||||
if (maxValue < i.val) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (i.children && i.children.length) {
|
||||
if (!this.getData(i.children, result, i.val)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
async save() {
|
||||
let data = {
|
||||
rebate_id: this.selectedRow.id
|
||||
};
|
||||
let tmp = [];
|
||||
if (!this.getData(this.treeNodes, tmp, this.selectedRow.hierarchy_value)) {
|
||||
return;
|
||||
}
|
||||
data.json = JSON.stringify(tmp);
|
||||
this.saveLoading = true;
|
||||
const resp = await saveRebateSet(data);
|
||||
this.saveLoading = false;
|
||||
if (resp.code == 0) {
|
||||
this.$cusModal.showModal({
|
||||
type: 'message',
|
||||
status: 'success',
|
||||
contentText: this.$t('common.success.action')
|
||||
});
|
||||
} else {
|
||||
this.$cusModal.showModal({
|
||||
type: 'message',
|
||||
status: 'warning',
|
||||
contentText: resp.msg ?? this.$t('common.error.sysError')
|
||||
});
|
||||
}
|
||||
},
|
||||
isEmpty(str) {
|
||||
if (typeof value === 'number' && !isNaN(str)) str = str + '';
|
||||
if (str == 'undefined' || str == 'null' || str == null || str == '' || str == undefined) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
returnFloat(value) {
|
||||
if (this.isEmpty(value)) return '0.00';
|
||||
var value = Math.round(parseFloat(value) * 100) / 100;
|
||||
var s = value.toString().split('.');
|
||||
if (s.length == 1) {
|
||||
value = value.toString() + '.00';
|
||||
return value;
|
||||
}
|
||||
if (s.length > 1) {
|
||||
if (s[1].length < 2) {
|
||||
value = value.toString() + '0';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* searchType n=按名字,v=按值
|
||||
*/
|
||||
query() {
|
||||
this.searchResult = null;
|
||||
let str = this.searchValue;
|
||||
if (this.isEmpty(str)) {
|
||||
this.searchResult = null;
|
||||
} else {
|
||||
let tmp = [];
|
||||
walkTreeData(
|
||||
this.treeNodes,
|
||||
(node, index, parent) => {
|
||||
if (this.searchType == 'n') {
|
||||
if (node.text.indexOf(str) >= 0) {
|
||||
tmp.push(node);
|
||||
}
|
||||
} else if (this.searchType == 'v') {
|
||||
str = parseFloat(str);
|
||||
let ibvalue = node.val;
|
||||
if (this.isEmpty(ibvalue)) {
|
||||
ibvalue = 0;
|
||||
} else {
|
||||
ibvalue = parseFloat(ibvalue);
|
||||
}
|
||||
if (ibvalue == str) {
|
||||
tmp.push(node);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
childrenKey: 'children',
|
||||
reverse: false,
|
||||
childFirst: false
|
||||
}
|
||||
);
|
||||
this.searchResult = tmp;
|
||||
this.$refs.resultDialog.open();
|
||||
}
|
||||
},
|
||||
handleRow(rowData) {
|
||||
this.closeSearchDialog();
|
||||
this.$refs.tree.openNodeAndParents(rowData);
|
||||
const query = uni.createSelectorQuery();
|
||||
query.select('#refN' + rowData.id).boundingClientRect();
|
||||
query.select('.container').boundingClientRect();
|
||||
query.exec((res) => {
|
||||
this.scrollTop = this.curScrollTop + res[0].top - res[1].height - res[1].top;
|
||||
});
|
||||
},
|
||||
hideSearchDialog() {
|
||||
if (!this.searchResult || !this.searchResult.length) {
|
||||
this.closeSearchDialog()
|
||||
}
|
||||
},
|
||||
closeSearchDialog() {
|
||||
this.$refs.resultDialog.close();
|
||||
},
|
||||
handleScroll(e) {
|
||||
this.curScrollTop = e.detail.scrollTop;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const userStore = useUserStore();
|
||||
this.selectedRow = userStore.currentRow;
|
||||
this.loadData(this.selectedRow);
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query
|
||||
.select('.container')
|
||||
.boundingClientRect((data) => {
|
||||
if (data) {
|
||||
this.contentHeight = `calc(100vh - ${data.height + 100}px - 200upx)`;
|
||||
}
|
||||
})
|
||||
.exec();
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './tree.scss';
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.tree-node {
|
||||
.uni-easyinput__content-input {
|
||||
height: 50upx !important;
|
||||
}
|
||||
}
|
||||
|
||||
.t-input-error {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
.diff {
|
||||
padding-left: 16upx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.error-diff {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user