Przeglądaj źródła

新增:后台展示预计收益;

dodo hold 2 lat temu
rodzic
commit
46b228602e

+ 1 - 1
.env.production

@@ -1,2 +1,2 @@
-VITE_API_BASE_URL= 'https://api.molilian.com'
+VITE_API_BASE_URL= 'https://adminapi.molilian.com'
 VITE_API_BASE_SCOPE = 'admin'

+ 133 - 92
src/api/taobao.ts

@@ -1,155 +1,196 @@
-
 import axios from 'axios';
 import qs from 'query-string';
 import type { DescData } from '@arco-design/web-vue/es/descriptions/interface';
-import type { ListRes } from './base'
+import type { ListRes } from './base';
 
 export interface UpdateCookieModel {
-  cookie: string;
+    cookie: string;
 }
 
 export interface FormRes {
-  msg: string;
-  code: number;
-  success: boolean;
+    msg: string;
+    code: number;
+    success: boolean;
 }
 
 export function updateCookies(data: UpdateCookieModel) {
-  return axios.post<FormRes>('/api/taobao/updateCookies', data);
+    return axios.post<FormRes>('/api/taobao/updateCookies', data);
 }
 
 // list
 export interface TkPoolRecord {
-  id: number;
-  cookies: string;
-  create_time: Date;
-  daily_income_limit: number;
-  current_amt: number;
-  description: string;
-  floorId: string;
-  last_time: Date;
-  nodeName: string;
-  refpid: string;
-  status: '' | 'online' | 'offline';
-  tb_token: string;
+    id: number;
+    name: string;
+    company: string;
+    phone: string;
+    cookies: string;
+    create_time: Date;
+    daily_income_limit: number;
+    current_amt: number;
+    description: string;
+    floorId: string;
+    last_time: Date;
+    nodeName: string;
+    refpid: string;
+    status: '' | 'online' | 'offline';
+    tb_token: string;
 }
 export interface TkPoolParams extends Partial<TkPoolRecord> {
-  current: number;
-  pageSize: number;
-  sort?: string;
-  order?: string;
-  getTotal: boolean;
+    current: number;
+    pageSize: number;
+    sort?: string;
+    order?: string;
+    getTotal: boolean;
 }
 
 export interface TkPoolRes extends ListRes {
-  list: TkPoolRecord[];
+    list: TkPoolRecord[];
 }
 
 export function queryTkPool(data: TkPoolParams) {
-  return axios.post<TkPoolRes>('/api/taobao/list', data);
+    return axios.post<TkPoolRes>('/api/taobao/list', data);
 }
 
 /*
 daily_logs
 */
 export interface DailyLogRecord {
-  id: number;
-  log_date: Date;
-  channel: number;
-  total_count: number;
-  abandon_count: number;
-  abandon_percentage: string;
-  success_count: number;
-  success_percentage: string;
-  create_time: Date;
-  last_time: Date;
+    id: number;
+    log_date: Date;
+    channel: number;
+    total_count: number;
+    abandon_count: number;
+    abandon_percentage: string;
+    success_count: number;
+    success_percentage: string;
+    create_time: Date;
+    last_time: Date;
 }
 
 export interface DailyLogParams extends Partial<DailyLogRecord> {
-  query_date?: string[]; // 覆盖 report_date 的类型为字符串数组
-  accountName?: string;
-  current: number;
-  pageSize: number;
-  sort?: string;
-  order?: string;
+    query_date?: string[]; // 覆盖 report_date 的类型为字符串数组
+    accountName?: string;
+    current: number;
+    pageSize: number;
+    sort?: string;
+    order?: string;
 }
 export interface DailyLogRes extends ListRes {
-  list: DailyLogRecord[];
+    list: DailyLogRecord[];
 }
 export function queryDailyLog(data: DailyLogParams) {
-  return axios.post<DailyLogRes>('/api/report/daily_list', data);
+    return axios.post<DailyLogRes>('/api/report/daily_list', data);
 }
 
 /*
 tk_report_hourtrend
 */
 export interface TkReportRecord {
-  id: number;
-  accountName: string;
-  report_date: Date;
-  isLeaf?: boolean;
-  create_time: Date;
-  last_time: Date;
-  uclk_uv_56: number;
-  uclk_pv_56: number;
-  uclt_uv_56: number;
-  ucart_uv_56: number;
-  dep_ord_num_56: number;
-  dep_ord_dep_amt_56: number;
-  pay_ord_num_56: number;
-  pay_ord_amt_56: number;
-  pay_tk_disp_tfee_56: number;
-  pay_ord_uv_56: number;
-  eff_ord_num: number;
-  eff_ord_amt: number;
-  eff_tk_disp_tfee: number;
-  sett_ord_num_56: number;
-  sett_ord_amt_56: number;
-  sett_tk_disp_tfee_56: number;
-
-  total_count: number;
-  abandon_count: number;
-  success_count: number;
-  abandon_percentage: string;
-  success_percentage: string;
+    id: number;
+    accountName: string;
+    report_date: Date;
+    isLeaf?: boolean;
+    create_time: Date;
+    last_time: Date;
+    uclk_uv_56: number;
+    uclk_pv_56: number;
+    uclt_uv_56: number;
+    ucart_uv_56: number;
+    dep_ord_num_56: number;
+    dep_ord_dep_amt_56: number;
+    pay_ord_num_56: number;
+    pay_ord_amt_56: number;
+    pay_tk_disp_tfee_56: number;
+    pay_ord_uv_56: number;
+    eff_ord_num: number;
+    eff_ord_amt: number;
+    eff_tk_disp_tfee: number;
+    sett_ord_num_56: number;
+    sett_ord_amt_56: number;
+    sett_tk_disp_tfee_56: number;
+
+    total_count: number;
+    abandon_count: number;
+    success_count: number;
+    abandon_percentage: string;
+    success_percentage: string;
 }
 
-
 export interface TkReportParams extends Partial<TkReportRecord> {
-  accountName?: string;
-  query_date?: string[]; // 覆盖 report_date 的类型为字符串数组
-  current: number;
-  pageSize: number;
-  sort?: string;
-  order?: string;
+    accountName?: string;
+    query_date?: string[]; // 覆盖 report_date 的类型为字符串数组
+    current: number;
+    pageSize: number;
+    sort?: string;
+    order?: string;
 }
 
-
 export interface TkReportRes extends ListRes {
-  list: TkReportRecord[];
+    list: TkReportRecord[];
 }
 export function queryTkReport(data: TkReportParams) {
-  return axios.post<TkReportRes>('/api/taobao/report_list', data);
+    return axios.post<TkReportRes>('/api/taobao/report_list', data);
 }
 export function queryTkReportDailys(data: TkReportParams) {
-  return axios.post<TkReportRes>('/api/taobao/report_dailys', data);
+    return axios.post<TkReportRes>('/api/taobao/report_dailys', data);
 }
 export function queryTkReportHourtrend(data: TkReportParams) {
-  return axios.post<TkReportRes>('/api/taobao/report_hourtrend', data);
+    return axios.post<TkReportRes>('/api/taobao/report_hourtrend', data);
 }
 
-
 export interface ChartDataResponse {
-  name: string;
-  value: number;
+    name: string;
+    value: number;
 }
 
 export interface TkReportChartRes {
-  code: number;
-  data: ChartDataResponse[];
-  data2: ChartDataResponse[];
+    code: number;
+    data: ChartDataResponse[];
+    data2: ChartDataResponse[];
 }
 
 export function queryTkReportChartData(data: TkReportParams) {
-  return axios.post<TkReportChartRes>('/api/taobao/chartData', data);
-}
+    return axios.post<TkReportChartRes>('/api/taobao/chartData', data);
+}
+
+export interface TkSettleBillRecord {
+    id: number;
+    accountName: string;
+    belongTime: Date;
+    create_time: Date;
+    last_time: Date;
+
+    bizType: number;
+    bizTypeDesc: string;
+
+    clearingPeriod: string;
+    expectedEarnings: number;
+
+    incomeType: number;
+    incomeTypeDesc: string;
+
+    settleIncomeTotal: number;
+    settleStatus: string;
+
+    dividedIncome: number;
+    platformServiceFee: number;
+}
+
+export interface TkSettleBillsParams extends Partial<TkSettleBillRecord> {
+    keyword?: string;
+    query_date?: string[]; // 覆盖 report_date 的类型为字符串数组
+    current: number;
+    pageSize: number;
+    sort?: string;
+    order?: string;
+}
+
+export interface TkSettleBillsRes extends ListRes {
+    list: TkSettleBillRecord[];
+}
+export function queryTkSettleBillsTotal(data: TkSettleBillsParams) {
+    return axios.post<TkSettleBillsRes>('/api/taobao/settle_bills_total', data);
+}
+export function queryTkSettleBills(data: TkSettleBillsParams) {
+    return axios.post<TkSettleBillsRes>('/api/taobao/settle_bills', data);
+}

+ 3 - 0
src/locale/en-US/taoke.ts

@@ -16,6 +16,8 @@ export default {
   'form.button.check': 'View',
 
   'menu.taobao.report_daily': 'Daily Report',
+  'menu.taobao.bill_dailys': 'Expected revenue',
+  'menu.taobao.orders': 'Orders',
 
   'menu.list.searchTable': 'Search Table',
   'searchTable.form.number': 'Set Number',
@@ -30,6 +32,7 @@ export default {
   'searchTable.form.filterType.artificial': 'artificial',
   'searchTable.form.filterType.rules': 'Rules',
   'searchTable.form.lastTime': 'Last Time',
+  'searchTable.form.order_date': 'Order Date',
   'searchTable.form.query_date': 'Report Date',
   'searchTable.form.status': 'Status',
   'searchTable.form.status.online': 'Online',

+ 3 - 0
src/locale/zh-CN/taoke.ts

@@ -16,6 +16,8 @@ export default {
   'form.button.withdraw': '撤回',
 
   'menu.taobao.report_daily': '收入报表',
+  'menu.taobao.bill_dailys': '联盟预计收益',
+  'menu.taobao.orders': '联盟推广订单',
 
   'menu.list.searchTable': '查询表格',
   'searchTable.form.number': '集合编号',
@@ -30,6 +32,7 @@ export default {
   'searchTable.form.filterType.artificial': '人工筛选',
   'searchTable.form.filterType.rules': '规则筛选',
   'searchTable.form.lastTime': '最后更新',
+  'searchTable.form.order_date': '订单日期',
   'searchTable.form.report_date': '报表日期',
   'searchTable.form.status': '状态',
   'searchTable.form.status.online': '已上线',

+ 59 - 39
src/router/routes/modules/taobao.ts

@@ -2,47 +2,67 @@ import { DEFAULT_LAYOUT } from '../base';
 import { AppRouteRecordRaw } from '../types';
 
 const TAOBAO: AppRouteRecordRaw = {
-  path: '/taobao',
-  name: 'taobao',
-  component: DEFAULT_LAYOUT,
-  meta: {
-    locale: 'menu.taobao',
-    requiresAuth: true,
-    icon: 'icon-menu',
-    order: 0,
-  },
-  children: [
-    {
-      path: 'updateCookies',
-      name: 'updateCookies',
-      component: () => import('@/views/taobao/updateCookies.vue'),
-      meta: {
-        locale: 'menu.taobao.updateCookies',
+    path: '/taobao',
+    name: 'taobao',
+    component: DEFAULT_LAYOUT,
+    meta: {
+        locale: 'menu.taobao',
         requiresAuth: true,
-        roles: ['*'],
-      },
+        icon: 'icon-menu',
+        order: 0,
     },
-    {
-      path: 'list',
-      name: 'taobao_list',
-      component: () => import('@/views/taobao/list.vue'),
-      meta: {
-        locale: 'menu.taobao.list',
-        requiresAuth: true,
-        roles: ['*'],
-      },
-    },
-    {
-      path: 'report_daily',
-      name: 'taobao_report_daily',
-      component: () => import('@/views/taobao/report_daily.vue'),
-      meta: {
-        locale: 'menu.taobao.report_daily',
-        requiresAuth: true,
-        roles: ['*'],
-      },
-    },
-  ],
+    children: [
+        {
+            path: 'updateCookies',
+            name: 'updateCookies',
+            component: () => import('@/views/taobao/updateCookies.vue'),
+            meta: {
+                locale: 'menu.taobao.updateCookies',
+                requiresAuth: true,
+                roles: ['*'],
+            },
+        },
+        {
+            path: 'list',
+            name: 'taobao_list',
+            component: () => import('@/views/taobao/list.vue'),
+            meta: {
+                locale: 'menu.taobao.list',
+                requiresAuth: true,
+                roles: ['*'],
+            },
+        },
+        {
+            path: 'report_daily',
+            name: 'taobao_report_daily',
+            component: () => import('@/views/taobao/report_daily.vue'),
+            meta: {
+                locale: 'menu.taobao.report_daily',
+                requiresAuth: true,
+                roles: ['*'],
+            },
+        },
+        {
+            path: 'bill_dailys',
+            name: 'taobao_bill_dailys',
+            component: () => import('@/views/taobao/bill_dailys.vue'),
+            meta: {
+                locale: 'menu.taobao.bill_dailys',
+                requiresAuth: true,
+                roles: ['*'],
+            },
+        },
+        // {
+        //     path: 'taobao_orders',
+        //     name: 'taobao_orders',
+        //     component: () => import('@/views/taobao/orders.vue'),
+        //     meta: {
+        //         locale: 'menu.taobao.orders',
+        //         requiresAuth: true,
+        //         roles: ['*'],
+        //     },
+        // },
+    ],
 };
 
 export default TAOBAO;

+ 530 - 0
src/views/taobao/bill_dailys.vue

@@ -0,0 +1,530 @@
+<template>
+    <div class="container">
+        <Breadcrumb :items="['menu.taobao', 'menu.taobao.bill_dailys']" />
+        <a-card class="general-card" :title="$t('menu.taobao.bill_dailys')">
+            <a-row>
+                <a-col :flex="1">
+                    <a-form :model="formModel" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
+                        label-align="left">
+                        <a-row :gutter="16">
+                            <a-col :span="8">
+                                <a-form-item field="keyword" label="账号名">
+                                    <!-- <a-input v-model="formModel.keyword" placeholder="指定账号名" /> -->
+
+                                    <a-select :model="formModel.keyword" placeholder="Please select ..."
+                                        @change="changeCompany">
+                                        <a-option value="">所有账号</a-option>
+                                        <a-option :key="item.id" v-for="item of accounts" :value="item.company"
+                                            :label="item.company" />
+                                    </a-select>
+                                </a-form-item>
+                            </a-col>
+                            <a-col :span="8">
+                                <a-form-item field="query_date" :label="$t('searchTable.form.report_date')">
+                                    <a-range-picker v-model="formModel.query_date" style="width: 100%"
+                                        @change="search" />
+                                </a-form-item>
+                            </a-col>
+                        </a-row>
+                    </a-form>
+                </a-col>
+                <a-divider style="height: 32px" direction="vertical" />
+                <a-col :flex="'86px'" style="text-align: right">
+                    <a-space direction="vertical" :size="18">
+                        <a-button type="primary" @click="search">
+                            <template #icon>
+                                <icon-search />
+                            </template>
+                            {{ $t('searchTable.form.search') }}
+                        </a-button>
+                    </a-space>
+                </a-col>
+            </a-row>
+            <a-divider style="margin-top: 0" />
+            <a-row style="margin-bottom: 16px">
+                <a-col :span="12"> </a-col>
+                <a-col :span="12" style="
+                        display: flex;
+                        align-items: center;
+                        justify-content: end;
+                    ">
+                    <a-tooltip :content="$t('searchTable.actions.refresh')">
+                        <div class="action-icon" @click="search"><icon-refresh size="18" /></div>
+                    </a-tooltip>
+                    <a-dropdown @select="handleSelectDensity">
+                        <a-tooltip :content="$t('searchTable.actions.density')">
+                            <div class="action-icon"><icon-line-height size="18" /></div>
+                        </a-tooltip>
+                        <template #content>
+                            <a-doption v-for="item in densityList" :key="item.value" :value="item.value"
+                                :class="{ active: item.value === size }">
+                                <span>{{ item.name }}</span>
+                            </a-doption>
+                        </template>
+                    </a-dropdown>
+                    <a-tooltip :content="$t('searchTable.actions.columnSetting')">
+                        <a-popover trigger="click" position="bl" @popup-visible-change="popupVisibleChange">
+                            <div class="action-icon"><icon-settings size="18" /></div>
+                            <template #content>
+                                <div id="tableSetting">
+                                    <div v-for="(item, index) in showColumns" :key="item.dataIndex" class="setting">
+                                        <div style="
+                                                margin-right: 4px;
+                                                cursor: move;
+                                            ">
+                                            <icon-drag-arrow />
+                                        </div>
+                                        <div>
+                                            <a-checkbox v-model="item.checked" @change="
+                                                handleChange(
+                                                    $event,
+                                                    item as TableColumnData,
+                                                    index
+                                                )
+                                                ">
+                                            </a-checkbox>
+                                        </div>
+                                        <div class="title">
+                                            {{
+                                                item.title === '#'
+                                                    ? '序列号'
+                                                    : item.title
+                                            }}
+                                        </div>
+                                    </div>
+                                </div>
+                            </template>
+                        </a-popover>
+                    </a-tooltip>
+                </a-col>
+            </a-row>
+
+
+            <a-table row-key="id" :loading="loading" :pagination="pagination" :summary="true" summary-text="汇总"
+                :columns="(cloneColumns as TableColumnData[])" :scroll="scroll" :scrollbar="scrollbar"
+                :data="renderData" :bordered="false" :size="size" :load-more="loadMore" @page-change="onPageChange">
+                <template #xAxis="{ record }">
+                    {{ dayjs(record.belongTime).format('YYYY-MM-DD') }}
+                </template>
+                <template #summary-cell="{ column, record }">
+                    <div>{{ toAmount(record[column.dataIndex]) }}</div>
+                </template>
+
+                <template #total_count="{ record }">
+                    <a-popover v-if="record.total_count > 0" popup-container="body" trigger="click">
+                        <a-tag color="green">{{ record.total_count }}</a-tag>
+                        <template #content>
+                            <CategoriesPercent :record="record" />
+                        </template>
+                    </a-popover>
+                </template>
+            </a-table>
+        </a-card>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { useRouter } from 'vue-router';
+import dayjs from 'dayjs';
+import { computed, ref, reactive, watch, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+import useLoading from '@/hooks/loading';
+import {
+    TkSettleBillRecord,
+    TkSettleBillsParams,
+    queryTkSettleBills,
+    queryTkSettleBillsTotal,
+
+    TkPoolRecord,
+    TkPoolParams,
+    queryTkPool,
+    TkSettleBillsRes
+} from '@/api/taobao';
+
+
+
+import { Pagination } from '@/types/global';
+import type {
+    TableData,
+    TableColumnData,
+} from '@arco-design/web-vue/es/table/interface';
+import cloneDeep from 'lodash/cloneDeep';
+import Sortable from 'sortablejs';
+import CategoriesPercent from './components/categories-percent.vue';
+
+type SizeProps = 'mini' | 'small' | 'medium' | 'large';
+type Column = TableColumnData & { checked?: true };
+
+const generateFormModel = () => {
+    let keyword = ''
+    if (router.currentRoute.value.query.name) {
+        keyword = router.currentRoute.value.query.name.toString()
+    }
+
+    return {
+        keyword,
+        query_date: [],
+        current: 1,
+        pageSize: 999,
+        sort: 'belongTime',
+        order: 'descending',
+    };
+};
+
+
+const router = useRouter();
+
+const { loading, setLoading } = useLoading(true);
+const { t } = useI18n();
+const renderData = ref<TkSettleBillRecord[]>([]);
+const formModel = ref(generateFormModel());
+const cloneColumns = ref<Column[]>([]);
+const showColumns = ref<Column[]>([]);
+const accounts = ref<TkPoolRecord[]>([]);
+const size = ref<SizeProps>('medium');
+
+const scrollbar = ref(true);
+const scroll = {
+    x: '100%',
+    y: '100%',
+    maxHeight: 'calc( 100vh - 420px)',
+};
+
+const basePagination: Pagination = {
+    current: 1,
+    pageSize: 100,
+};
+const pagination = reactive({
+    ...basePagination,
+});
+const densityList = computed(() => [
+    {
+        name: t('searchTable.size.mini'),
+        value: 'mini',
+    },
+    {
+        name: t('searchTable.size.small'),
+        value: 'small',
+    },
+    {
+        name: t('searchTable.size.medium'),
+        value: 'medium',
+    },
+    {
+        name: t('searchTable.size.large'),
+        value: 'large',
+    },
+]);
+
+const columns = computed<TableColumnData[]>(() => [
+    {
+        title: '日期',
+        dataIndex: 'xAxis',
+        slotName: 'xAxis',
+        fixed: 'left',
+        width: 150,
+    },
+    {
+        title: '所属账号',
+        dataIndex: 'accountName',
+        width: 150,
+    },
+    {
+        title: '结算总收入',
+        dataIndex: 'settleIncomeTotal',
+        slotName: 'settleIncomeTotal',
+        width: 140,
+        align: "right",
+    },
+    {
+        title: '他方分成',
+        dataIndex: 'dividedIncome',
+        slotName: 'dividedIncome',
+        width: 140,
+        align: "right",
+    },
+    {
+        title: '平台服务费',
+        dataIndex: 'platformServiceFee',
+        slotName: 'platformServiceFee',
+        width: 140,
+        align: "right",
+    },
+
+    {
+        title: '预计收益',
+        dataIndex: 'expectedEarnings',
+        slotName: 'expectedEarnings',
+        width: 140,
+        align: "right",
+    },
+    {
+        title: '状态',
+        dataIndex: 'settleStatus',
+        width: 120,
+    },
+    {
+        title: '业务类型',
+        dataIndex: 'bizTypeDesc',
+        width: 120,
+    },
+    {
+        title: '收益类型',
+        dataIndex: 'incomeTypeDesc',
+        width: 120,
+    },
+    {
+        title: '最后更新时间',
+        dataIndex: 'last_time',
+        width: 180,
+    },
+]);
+
+const toAmount = (val: any): any => {
+    console.log(val);
+    if (typeof val === 'number' && !Number.isNaN(val)) {
+        return parseFloat(val.toFixed(2));
+    }
+    return val;
+}
+
+const loadAccounts = async (
+    params: TkPoolParams = {
+        current: 1,
+        pageSize: 9999,
+        sort: 'status',
+        order: 'descending',
+        getTotal: false,
+    }
+) => {
+    setLoading(true);
+    try {
+        const { data } = await queryTkPool(params);
+        console.log(data);
+        if (!data) return;
+        // const { data } = await queryTkPool(params);
+        accounts.value = data.list;
+    } catch (err) {
+        console.log(err);
+        // you can report use errorHandler or other
+    } finally {
+        setLoading(false);
+    }
+};
+
+
+const fetchData = async (
+    params: TkSettleBillsParams = {
+        current: 1,
+        pageSize: 100,
+        sort: 'belongTime',
+        order: 'descending',
+    }
+) => {
+    setLoading(true);
+    try {
+        console.log(params)
+        if (params.keyword) {
+            const { data } = await queryTkSettleBills(params);
+            if (!data) return;
+
+            const list = data.list.map((item: TkSettleBillRecord) => ({
+                ...item,
+                isLeaf: true,
+            }));
+            renderData.value = list;
+            pagination.current = data.page;
+            pagination.total = data.count;
+        } else {
+            const { data } = await queryTkSettleBillsTotal(params);
+            if (!data) return;
+            renderData.value = data.list;
+            pagination.current = data.page;
+            pagination.total = data.count;
+        }
+    } catch (err) {
+        console.log(err);
+        // you can report use errorHandler or other
+    } finally {
+        setLoading(false);
+    }
+};
+
+
+const loadMore = async (
+    row: TableData,
+    done: (data: TableData[]) => void
+) => {
+    setLoading(true);
+    try {
+        let list: TableData[] = [];
+
+        if (row.accountName === '') {
+            console.log(row.belongTime)
+            const params: TkSettleBillsParams = {
+                query_date: [row.belongTime, row.belongTime],
+                current: 1,
+                pageSize: 999,
+                sort: 'belongTime',
+                order: 'descending',
+            };
+
+            const { data } = await queryTkSettleBills(params);
+            console.log(data);
+            if (!data) return;
+            list = data.list.map((item: TkSettleBillRecord) => ({
+                ...item,
+                isLeaf: true,
+            }));
+
+            console.log(list)
+        }
+        done(list);
+    } catch (err) {
+        console.log(err);
+        // you can report use errorHandler or other
+    } finally {
+        setLoading(false);
+    }
+};
+
+const search = () => {
+    fetchData({
+        ...basePagination,
+        ...formModel.value,
+    } as unknown as TkSettleBillsParams);
+};
+const changeCompany = (value: any) => {
+    const reset = () => {
+        formModel.value = generateFormModel();
+    };
+    formModel.value.keyword = value
+
+    fetchData({
+        ...basePagination,
+        ...formModel.value,
+    } as unknown as TkSettleBillsParams);
+};
+
+
+
+
+const onPageChange = (current: number) => {
+    fetchData({ ...basePagination, current });
+};
+
+loadAccounts()
+fetchData({ ...formModel.value } as unknown as TkSettleBillsParams);
+const reset = () => {
+    formModel.value = generateFormModel();
+};
+
+const handleSelectDensity = (
+    val: string | number | Record<string, any> | undefined,
+    e: Event
+) => {
+    size.value = val as SizeProps;
+};
+
+const handleChange = (
+    checked: boolean | (string | boolean | number)[],
+    column: Column,
+    index: number
+) => {
+    if (!checked) {
+        cloneColumns.value = showColumns.value.filter(
+            (item) => item.dataIndex !== column.dataIndex
+        );
+    } else {
+        cloneColumns.value.splice(index, 0, column);
+    }
+};
+
+const exchangeArray = <T extends Array<any>>(
+    array: T,
+    beforeIdx: number,
+    newIdx: number,
+    isDeep = false
+): T => {
+    const newArray = isDeep ? cloneDeep(array) : array;
+    if (beforeIdx > -1 && newIdx > -1) {
+        // 先替换后面的,然后拿到替换的结果替换前面的
+        newArray.splice(
+            beforeIdx,
+            1,
+            newArray.splice(newIdx, 1, newArray[beforeIdx]).pop()
+        );
+    }
+    return newArray;
+};
+
+const popupVisibleChange = (val: boolean) => {
+    if (val) {
+        nextTick(() => {
+            const el = document.getElementById(
+                'tableSetting'
+            ) as HTMLElement;
+            const sortable = new Sortable(el, {
+                onEnd(e: any) {
+                    const { oldIndex, newIndex } = e;
+                    exchangeArray(cloneColumns.value, oldIndex, newIndex);
+                    exchangeArray(showColumns.value, oldIndex, newIndex);
+                },
+            });
+        });
+    }
+};
+
+watch(
+    () => columns.value,
+    (val) => {
+        cloneColumns.value = cloneDeep(val);
+        cloneColumns.value.forEach((item, index) => {
+            item.checked = true;
+        });
+        showColumns.value = cloneDeep(cloneColumns.value);
+    },
+    { deep: true, immediate: true }
+);
+</script>
+
+<script lang="ts">
+export default {
+    name: 'bill_dailys',
+};
+</script>
+
+<style scoped lang="less">
+.container {
+    padding: 0 20px 20px 20px;
+}
+
+:deep(.arco-table-th) {
+    &:last-child {
+        .arco-table-th-item-title {
+            margin-left: 16px;
+        }
+    }
+}
+
+.action-icon {
+    margin-left: 12px;
+    cursor: pointer;
+}
+
+.active {
+    color: #0960bd;
+    background-color: #e3f4fc;
+}
+
+.setting {
+    display: flex;
+    align-items: center;
+    width: 200px;
+
+    .title {
+        margin-left: 12px;
+        cursor: pointer;
+    }
+}
+</style>

+ 290 - 356
src/views/taobao/list.vue

@@ -4,44 +4,23 @@
         <a-card class="general-card" :title="$t('menu.taobao.list')">
             <a-row>
                 <a-col :flex="1">
-                    <a-form
-                        :model="formModel"
-                        :label-col-props="{ span: 6 }"
-                        :wrapper-col-props="{ span: 18 }"
-                        label-align="left"
-                    >
+                    <a-form :model="formModel" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
+                        label-align="left">
                         <a-row :gutter="16">
                             <a-col :span="8">
                                 <a-form-item field="name" label="账号名">
-                                    <a-input
-                                        v-model="formModel.name"
-                                        placeholder="输入关键词搜索"
-                                    />
+                                    <a-input v-model="formModel.name" placeholder="输入关键词搜索" />
                                 </a-form-item>
                             </a-col>
                             <a-col :span="8">
-                                <a-form-item
-                                    field="lastTime"
-                                    :label="$t('searchTable.form.lastTime')"
-                                >
-                                    <a-range-picker
-                                        v-model="formModel.lastTime"
-                                        style="width: 100%"
-                                    />
+                                <a-form-item field="lastTime" :label="$t('searchTable.form.lastTime')">
+                                    <a-range-picker v-model="formModel.lastTime" style="width: 100%" />
                                 </a-form-item>
                             </a-col>
                             <a-col :span="8">
-                                <a-form-item
-                                    field="status"
-                                    :label="$t('searchTable.form.status')"
-                                >
-                                    <a-select
-                                        v-model="formModel.status"
-                                        :options="statusOptions"
-                                        :placeholder="
-                                            $t('searchTable.form.selectDefault')
-                                        "
-                                    />
+                                <a-form-item field="status" :label="$t('searchTable.form.status')">
+                                    <a-select v-model="formModel.status" :options="statusOptions" :placeholder="$t('searchTable.form.selectDefault')
+                                        " />
                                 </a-form-item>
                             </a-col>
                         </a-row>
@@ -71,73 +50,45 @@
                         </a-button>
                     </a-space>
                 </a-col>
-                <a-col
-                    :span="12"
-                    style="
+                <a-col :span="12" style="
                         display: flex;
                         align-items: center;
                         justify-content: end;
-                    "
-                >
+                    ">
                     <a-tooltip :content="$t('searchTable.actions.refresh')">
-                        <div class="action-icon" @click="search"
-                            ><icon-refresh size="18"
-                        /></div>
+                        <div class="action-icon" @click="search"><icon-refresh size="18" /></div>
                     </a-tooltip>
                     <a-dropdown @select="handleSelectDensity">
                         <a-tooltip :content="$t('searchTable.actions.density')">
-                            <div class="action-icon"
-                                ><icon-line-height size="18"
-                            /></div>
+                            <div class="action-icon"><icon-line-height size="18" /></div>
                         </a-tooltip>
                         <template #content>
-                            <a-doption
-                                v-for="item in densityList"
-                                :key="item.value"
-                                :value="item.value"
-                                :class="{ active: item.value === size }"
-                            >
+                            <a-doption v-for="item in densityList" :key="item.value" :value="item.value"
+                                :class="{ active: item.value === size }">
                                 <span>{{ item.name }}</span>
                             </a-doption>
                         </template>
                     </a-dropdown>
-                    <a-tooltip
-                        :content="$t('searchTable.actions.columnSetting')"
-                    >
-                        <a-popover
-                            trigger="click"
-                            position="bl"
-                            @popup-visible-change="popupVisibleChange"
-                        >
-                            <div class="action-icon"
-                                ><icon-settings size="18"
-                            /></div>
+                    <a-tooltip :content="$t('searchTable.actions.columnSetting')">
+                        <a-popover trigger="click" position="bl" @popup-visible-change="popupVisibleChange">
+                            <div class="action-icon"><icon-settings size="18" /></div>
                             <template #content>
                                 <div id="tableSetting">
-                                    <div
-                                        v-for="(item, index) in showColumns"
-                                        :key="item.dataIndex"
-                                        class="setting"
-                                    >
-                                        <div
-                                            style="
+                                    <div v-for="(item, index) in showColumns" :key="item.dataIndex" class="setting">
+                                        <div style="
                                                 margin-right: 4px;
                                                 cursor: move;
-                                            "
-                                        >
+                                            ">
                                             <icon-drag-arrow />
                                         </div>
                                         <div>
-                                            <a-checkbox
-                                                v-model="item.checked"
-                                                @change="
-                                                    handleChange(
-                                                        $event,
-                                                        item as TableColumnData,
-                                                        index
-                                                    )
-                                                "
-                                            >
+                                            <a-checkbox v-model="item.checked" @change="
+                                                handleChange(
+                                                    $event,
+                                                    item as TableColumnData,
+                                                    index
+                                                )
+                                                ">
                                             </a-checkbox>
                                         </div>
                                         <div class="title">
@@ -155,57 +106,36 @@
                 </a-col>
             </a-row>
 
-            <a-table
-                row-key="id"
-                :loading="loading"
-                :pagination="pagination"
-                :columns="(cloneColumns as TableColumnData[])"
-                :data="renderData"
-                :bordered="false"
-                :size="size"
-                @page-change="onPageChange"
-            >
-                <template #index="{ rowIndex }">
-                    {{
-                        rowIndex +
-                        1 +
-                        (pagination.current - 1) * pagination.pageSize
-                    }}
+            <a-table row-key="id" :loading="loading" :pagination="pagination"
+                :columns="(cloneColumns as TableColumnData[])" :data="renderData" :bordered="false" :size="size"
+                @page-change="onPageChange">
+                <template #index="{ record }">
+                    {{ record.id }}
                 </template>
                 <template #name="{ record }">
-                    <a-list-item-meta
-                        :title="record.name"
-                        :description="record.description"
-                    />
+                    <a-list-item-meta :title="record.company" :description="record.description" />
                 </template>
                 <template #daily_income_limit="{ record }">
-                    <a-tag
-                        bordered
-                        :color="
-                            record.current_amt > record.daily_income_limit &&
-                            record.daily_income_limit > 0
-                                ? 'red'
-                                : ''
-                        "
-                    >
+                    <a-tag bordered :color="record.current_amt > record.daily_income_limit &&
+                        record.daily_income_limit > 0
+                        ? 'red'
+                        : ''
+                        ">
                         {{ record.current_amt }} /
                         {{ record.daily_income_limit }}
                     </a-tag>
                 </template>
                 <template #status="{ record }">
-                    <a-tag v-if="record.status" bordered color="green"
-                        >在线</a-tag
-                    >
+                    <a-tag v-if="record.status" bordered color="green">在线</a-tag>
                     <a-tag v-else bordered color="">下线</a-tag>
                 </template>
                 <template #operations="{ record }">
-                    <a-button
-                        type="text"
-                        size="small"
-                        @click="changeItem(record)"
-                    >
+                    <a-button type="text" size="small" @click="changeItem(record)">
                         更新
                     </a-button>
+                    <a-button type="text" size="small" @click="billView(record)">
+                        预计收益
+                    </a-button>
                 </template>
             </a-table>
         </a-card>
@@ -213,279 +143,283 @@
 </template>
 
 <script lang="ts" setup>
-    import { useRouter } from 'vue-router';
-    import { computed, ref, reactive, watch, nextTick } from 'vue';
-    import { useI18n } from 'vue-i18n';
-    import useLoading from '@/hooks/loading';
-    import { queryTkPool, TkPoolRecord, TkPoolParams } from '@/api/taobao';
-    import { Pagination } from '@/types/global';
-    import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
-    import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
-    import cloneDeep from 'lodash/cloneDeep';
-    import Sortable from 'sortablejs';
+import { useRouter } from 'vue-router';
+import { computed, ref, reactive, watch, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+import useLoading from '@/hooks/loading';
+import { queryTkPool, TkPoolRecord, TkPoolParams } from '@/api/taobao';
+import { Pagination } from '@/types/global';
+import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
+import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
+import cloneDeep from 'lodash/cloneDeep';
+import Sortable from 'sortablejs';
 
-    type SizeProps = 'mini' | 'small' | 'medium' | 'large';
-    type Column = TableColumnData & { checked?: true };
+type SizeProps = 'mini' | 'small' | 'medium' | 'large';
+type Column = TableColumnData & { checked?: true };
 
-    const generateFormModel = () => {
-        return {
-            name: '',
-            lastTime: [],
-            status: '',
-            sort: 'status',
-            order: 'descending',
-        };
+const generateFormModel = () => {
+    return {
+        name: '',
+        lastTime: [],
+        status: '',
+        sort: 'status',
+        order: 'descending',
     };
-    const router = useRouter();
+};
+const router = useRouter();
 
-    const { loading, setLoading } = useLoading(true);
-    const { t } = useI18n();
-    const renderData = ref<TkPoolRecord[]>([]);
-    const formModel = ref(generateFormModel());
-    const cloneColumns = ref<Column[]>([]);
-    const showColumns = ref<Column[]>([]);
+const { loading, setLoading } = useLoading(true);
+const { t } = useI18n();
+const renderData = ref<TkPoolRecord[]>([]);
+const formModel = ref(generateFormModel());
+const cloneColumns = ref<Column[]>([]);
+const showColumns = ref<Column[]>([]);
 
-    const size = ref<SizeProps>('medium');
+const size = ref<SizeProps>('medium');
 
-    const basePagination: Pagination = {
-        current: 1,
-        pageSize: 20,
-    };
-    const pagination = reactive({
-        ...basePagination,
-    });
-    const densityList = computed(() => [
-        {
-            name: t('searchTable.size.mini'),
-            value: 'mini',
-        },
-        {
-            name: t('searchTable.size.small'),
-            value: 'small',
-        },
-        {
-            name: t('searchTable.size.medium'),
-            value: 'medium',
-        },
-        {
-            name: t('searchTable.size.large'),
-            value: 'large',
-        },
-    ]);
+const basePagination: Pagination = {
+    current: 1,
+    pageSize: 20,
+};
+const pagination = reactive({
+    ...basePagination,
+});
+const densityList = computed(() => [
+    {
+        name: t('searchTable.size.mini'),
+        value: 'mini',
+    },
+    {
+        name: t('searchTable.size.small'),
+        value: 'small',
+    },
+    {
+        name: t('searchTable.size.medium'),
+        value: 'medium',
+    },
+    {
+        name: t('searchTable.size.large'),
+        value: 'large',
+    },
+]);
 
-    const columns = computed<TableColumnData[]>(() => [
-        {
-            title: '#',
-            dataIndex: 'index',
-            slotName: 'index',
-            width: 50,
-        },
-        {
-            title: '账号名',
-            dataIndex: 'name',
-            slotName: 'name',
-        },
-        {
-            title: '创建时间',
-            dataIndex: 'create_time',
-        },
-        {
-            title: '最后更新',
-            dataIndex: 'last_time',
-        },
-        {
-            title: '每日收益限额',
-            dataIndex: 'daily_income_limit',
-            slotName: 'daily_income_limit',
-        },
-        {
-            title: '可提现余额',
-            dataIndex: 'draw_balance',
-            slotName: 'draw_balance',
-        },
-        {
-            title: '运行节点',
-            dataIndex: 'nodeName',
-        },
-        {
-            title: '状态',
-            dataIndex: 'status',
-            slotName: 'status',
-        },
-        {
-            title: '操作',
-            dataIndex: 'operations',
-            slotName: 'operations',
-        },
-    ]);
+const columns = computed<TableColumnData[]>(() => [
+    {
+        title: '#',
+        dataIndex: 'index',
+        slotName: 'index',
+        width: 50,
+    },
+    {
+        title: '账号名',
+        dataIndex: 'name',
+        slotName: 'name',
+    },
+    {
+        title: '创建时间',
+        dataIndex: 'create_time',
+    },
+    {
+        title: '最后更新',
+        dataIndex: 'last_time',
+    },
+    {
+        title: '每日收益限额',
+        dataIndex: 'daily_income_limit',
+        slotName: 'daily_income_limit',
+    },
+    {
+        title: '可提现余额',
+        dataIndex: 'draw_balance',
+        slotName: 'draw_balance',
+    },
+    {
+        title: '运行节点',
+        dataIndex: 'nodeName',
+    },
+    {
+        title: '状态',
+        dataIndex: 'status',
+        slotName: 'status',
+    },
+    {
+        title: '操作',
+        dataIndex: 'operations',
+        slotName: 'operations',
+    },
+]);
 
-    const statusOptions = computed<SelectOptionData[]>(() => [
-        {
-            label: '全部',
-            value: '',
-        },
-        {
-            label: '在线',
-            value: 'online',
-        },
-        {
-            label: '下线',
-            value: 'offline',
-        },
-    ]);
+const statusOptions = computed<SelectOptionData[]>(() => [
+    {
+        label: '全部',
+        value: '',
+    },
+    {
+        label: '在线',
+        value: 'online',
+    },
+    {
+        label: '下线',
+        value: 'offline',
+    },
+]);
 
-    const fetchData = async (
-        params: TkPoolParams = {
-            current: 1,
-            pageSize: 20,
-            sort: 'status',
-            order: 'descending',
-            getTotal: true,
-        }
-    ) => {
-        setLoading(true);
-        try {
-            const { data } = await queryTkPool(params);
-            console.log(data);
-            if (!data) return;
-            // const { data } = await queryTkPool(params);
-            renderData.value = data.list;
-            pagination.current = data.page;
-            pagination.total = data.count;
-            console.log(data);
-        } catch (err) {
-            console.log(err);
-            // you can report use errorHandler or other
-        } finally {
-            setLoading(false);
-        }
-    };
+const fetchData = async (
+    params: TkPoolParams = {
+        current: 1,
+        pageSize: 20,
+        sort: 'status',
+        order: 'descending',
+        getTotal: true,
+    }
+) => {
+    setLoading(true);
+    try {
+        const { data } = await queryTkPool(params);
+        console.log(data);
+        if (!data) return;
+        // const { data } = await queryTkPool(params);
+        renderData.value = data.list;
+        pagination.current = data.page;
+        pagination.total = data.count;
+        console.log(data);
+    } catch (err) {
+        console.log(err);
+        // you can report use errorHandler or other
+    } finally {
+        setLoading(false);
+    }
+};
 
-    const changeItem = async (data: TkPoolRecord) => {
-        router.push({ name: 'updateCookies' });
-    };
+const changeItem = async (data: TkPoolRecord) => {
+    router.push({ name: 'updateCookies' });
+};
 
-    const search = () => {
-        fetchData({
-            ...basePagination,
-            ...formModel.value,
-        } as unknown as TkPoolParams);
-    };
-    const onPageChange = (current: number) => {
-        fetchData({ ...basePagination, current, getTotal: current === 1 });
-    };
+const billView = async (data: TkPoolRecord) => {
+    router.push({ name: 'taobao_bill_dailys',query:{ name: data.company } });
+};
 
-    fetchData();
-    const reset = () => {
-        formModel.value = generateFormModel();
-    };
+const search = () => {
+    fetchData({
+        ...basePagination,
+        ...formModel.value,
+    } as unknown as TkPoolParams);
+};
+const onPageChange = (current: number) => {
+    fetchData({ ...basePagination, current, getTotal: current === 1 });
+};
 
-    const handleSelectDensity = (
-        val: string | number | Record<string, any> | undefined,
-        e: Event
-    ) => {
-        size.value = val as SizeProps;
-    };
+fetchData();
+const reset = () => {
+    formModel.value = generateFormModel();
+};
 
-    const handleChange = (
-        checked: boolean | (string | boolean | number)[],
-        column: Column,
-        index: number
-    ) => {
-        if (!checked) {
-            cloneColumns.value = showColumns.value.filter(
-                (item) => item.dataIndex !== column.dataIndex
-            );
-        } else {
-            cloneColumns.value.splice(index, 0, column);
-        }
-    };
+const handleSelectDensity = (
+    val: string | number | Record<string, any> | undefined,
+    e: Event
+) => {
+    size.value = val as SizeProps;
+};
 
-    const exchangeArray = <T extends Array<any>>(
-        array: T,
-        beforeIdx: number,
-        newIdx: number,
-        isDeep = false
-    ): T => {
-        const newArray = isDeep ? cloneDeep(array) : array;
-        if (beforeIdx > -1 && newIdx > -1) {
-            // 先替换后面的,然后拿到替换的结果替换前面的
-            newArray.splice(
-                beforeIdx,
-                1,
-                newArray.splice(newIdx, 1, newArray[beforeIdx]).pop()
-            );
-        }
-        return newArray;
-    };
+const handleChange = (
+    checked: boolean | (string | boolean | number)[],
+    column: Column,
+    index: number
+) => {
+    if (!checked) {
+        cloneColumns.value = showColumns.value.filter(
+            (item) => item.dataIndex !== column.dataIndex
+        );
+    } else {
+        cloneColumns.value.splice(index, 0, column);
+    }
+};
 
-    const popupVisibleChange = (val: boolean) => {
-        if (val) {
-            nextTick(() => {
-                const el = document.getElementById(
-                    'tableSetting'
-                ) as HTMLElement;
-                const sortable = new Sortable(el, {
-                    onEnd(e: any) {
-                        const { oldIndex, newIndex } = e;
-                        exchangeArray(cloneColumns.value, oldIndex, newIndex);
-                        exchangeArray(showColumns.value, oldIndex, newIndex);
-                    },
-                });
-            });
-        }
-    };
+const exchangeArray = <T extends Array<any>>(
+    array: T,
+    beforeIdx: number,
+    newIdx: number,
+    isDeep = false
+): T => {
+    const newArray = isDeep ? cloneDeep(array) : array;
+    if (beforeIdx > -1 && newIdx > -1) {
+        // 先替换后面的,然后拿到替换的结果替换前面的
+        newArray.splice(
+            beforeIdx,
+            1,
+            newArray.splice(newIdx, 1, newArray[beforeIdx]).pop()
+        );
+    }
+    return newArray;
+};
 
-    watch(
-        () => columns.value,
-        (val) => {
-            cloneColumns.value = cloneDeep(val);
-            cloneColumns.value.forEach((item, index) => {
-                item.checked = true;
+const popupVisibleChange = (val: boolean) => {
+    if (val) {
+        nextTick(() => {
+            const el = document.getElementById(
+                'tableSetting'
+            ) as HTMLElement;
+            const sortable = new Sortable(el, {
+                onEnd(e: any) {
+                    const { oldIndex, newIndex } = e;
+                    exchangeArray(cloneColumns.value, oldIndex, newIndex);
+                    exchangeArray(showColumns.value, oldIndex, newIndex);
+                },
             });
-            showColumns.value = cloneDeep(cloneColumns.value);
-        },
-        { deep: true, immediate: true }
-    );
+        });
+    }
+};
+
+watch(
+    () => columns.value,
+    (val) => {
+        cloneColumns.value = cloneDeep(val);
+        cloneColumns.value.forEach((item, index) => {
+            item.checked = true;
+        });
+        showColumns.value = cloneDeep(cloneColumns.value);
+    },
+    { deep: true, immediate: true }
+);
 </script>
 
 <script lang="ts">
-    export default {
-        name: 'SearchTable',
-    };
+export default {
+    name: 'SearchTable',
+};
 </script>
 
 <style scoped lang="less">
-    .container {
-        padding: 0 20px 20px 20px;
-    }
+.container {
+    padding: 0 20px 20px 20px;
+}
 
-    :deep(.arco-table-th) {
-        &:last-child {
-            .arco-table-th-item-title {
-                margin-left: 16px;
-            }
+:deep(.arco-table-th) {
+    &:last-child {
+        .arco-table-th-item-title {
+            margin-left: 16px;
         }
     }
+}
 
-    .action-icon {
-        margin-left: 12px;
-        cursor: pointer;
-    }
+.action-icon {
+    margin-left: 12px;
+    cursor: pointer;
+}
 
-    .active {
-        color: #0960bd;
-        background-color: #e3f4fc;
-    }
+.active {
+    color: #0960bd;
+    background-color: #e3f4fc;
+}
 
-    .setting {
-        display: flex;
-        align-items: center;
-        width: 200px;
+.setting {
+    display: flex;
+    align-items: center;
+    width: 200px;
 
-        .title {
-            margin-left: 12px;
-            cursor: pointer;
-        }
+    .title {
+        margin-left: 12px;
+        cursor: pointer;
     }
+}
 </style>

+ 444 - 0
src/views/taobao/orders.vue

@@ -0,0 +1,444 @@
+<template>
+    <div class="container">
+        <Breadcrumb :items="['menu.taobao', 'menu.taobao.orders']" />
+        <a-card class="general-card" :title="$t('menu.taobao.orders')">
+            <a-row>
+                <a-col :flex="1">
+                    <a-form :model="formModel" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
+                        label-align="left">
+                        <a-row :gutter="16">
+                            <a-col :span="8">
+                                <a-form-item field="name" label="账号名">
+                                    <a-input v-model="formModel.accountName" placeholder="输入关键词搜索" />
+                                </a-form-item>
+                            </a-col>
+                            <a-col :span="8">
+                                <a-form-item field="query_date" :label="$t('searchTable.form.order_date')">
+                                    <a-range-picker v-model="formModel.query_date" style="width: 100%" />
+                                </a-form-item>
+                            </a-col>
+                        </a-row>
+                    </a-form>
+                </a-col>
+                <a-divider style="height: 32px" direction="vertical" />
+                <a-col :flex="'86px'" style="text-align: right">
+                    <a-space direction="vertical" :size="18">
+                        <a-button type="primary" @click="search">
+                            <template #icon>
+                                <icon-search />
+                            </template>
+                            {{ $t('searchTable.form.search') }}
+                        </a-button>
+                    </a-space>
+                </a-col>
+            </a-row>
+            <a-divider style="margin-top: 0" />
+            <a-row style="margin-bottom: 16px">
+                <a-col :span="12"> </a-col>
+                <a-col :span="12" style="
+                        display: flex;
+                        align-items: center;
+                        justify-content: end;
+                    ">
+                    <a-tooltip :content="$t('searchTable.actions.refresh')">
+                        <div class="action-icon" @click="search"><icon-refresh size="18" /></div>
+                    </a-tooltip>
+                    <a-dropdown @select="handleSelectDensity">
+                        <a-tooltip :content="$t('searchTable.actions.density')">
+                            <div class="action-icon"><icon-line-height size="18" /></div>
+                        </a-tooltip>
+                        <template #content>
+                            <a-doption v-for="item in densityList" :key="item.value" :value="item.value"
+                                :class="{ active: item.value === size }">
+                                <span>{{ item.name }}</span>
+                            </a-doption>
+                        </template>
+                    </a-dropdown>
+                    <a-tooltip :content="$t('searchTable.actions.columnSetting')">
+                        <a-popover trigger="click" position="bl" @popup-visible-change="popupVisibleChange">
+                            <div class="action-icon"><icon-settings size="18" /></div>
+                            <template #content>
+                                <div id="tableSetting">
+                                    <div v-for="(item, index) in showColumns" :key="item.dataIndex" class="setting">
+                                        <div style="
+                                                margin-right: 4px;
+                                                cursor: move;
+                                            ">
+                                            <icon-drag-arrow />
+                                        </div>
+                                        <div>
+                                            <a-checkbox v-model="item.checked" @change="
+                                                handleChange(
+                                                    $event,
+                                                    item as TableColumnData,
+                                                    index
+                                                )
+                                                ">
+                                            </a-checkbox>
+                                        </div>
+                                        <div class="title">
+                                            {{
+                                                item.title === '#'
+                                                    ? '序列号'
+                                                    : item.title
+                                            }}
+                                        </div>
+                                    </div>
+                                </div>
+                            </template>
+                        </a-popover>
+                    </a-tooltip>
+                </a-col>
+            </a-row>
+
+            <a-table row-key="id" :loading="loading" :pagination="pagination"
+                :columns="(cloneColumns as TableColumnData[])" :scroll="scroll" :scrollbar="scrollbar"
+                :data="renderData" :bordered="false" :size="size" :load-more="loadMore" @page-change="onPageChange">
+                <template #xAxis="{ record }">
+                    <template v-if="record.accountName !== '' && !record.isLeaf">
+                        {{ record.accountName }}
+                    </template>
+                    <template v-else>
+                        {{ dayjs(record.belongTime).format(record.isLeaf ? 'HH:mm' : 'YYYY-MM-DD') }}
+                    </template>
+                </template>
+                <template #accountName="{ record }">
+                    {{ record.isLeaf ? '' : record.accountName }}
+                </template>
+                <template #total_count="{ record }">
+                    <a-popover v-if="record.total_count > 0" popup-container="body" trigger="click">
+                        <a-tag color="green">{{ record.total_count }}</a-tag>
+                        <template #content>
+                            <CategoriesPercent :record="record" />
+                        </template>
+                    </a-popover>
+                </template>
+            </a-table>
+        </a-card>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { useRouter } from 'vue-router';
+import dayjs from 'dayjs';
+import { computed, ref, reactive, watch, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+import useLoading from '@/hooks/loading';
+import {
+    queryTkSettleBills,
+    TkSettleBillRecord,
+    TkSettleBillsParams,
+} from '@/api/taobao';
+
+
+
+import { Pagination } from '@/types/global';
+import type {
+    TableData,
+    TableColumnData,
+} from '@arco-design/web-vue/es/table/interface';
+import cloneDeep from 'lodash/cloneDeep';
+import Sortable from 'sortablejs';
+import CategoriesPercent from './components/categories-percent.vue';
+
+type SizeProps = 'mini' | 'small' | 'medium' | 'large';
+type Column = TableColumnData & { checked?: true };
+
+const generateFormModel = () => {
+    return {
+        accountName: '',
+        query_date: [],
+        current: 1,
+        pageSize: 999,
+        sort: 'belongTime',
+        order: 'descending',
+    };
+};
+const router = useRouter();
+
+const { loading, setLoading } = useLoading(true);
+const { t } = useI18n();
+const renderData = ref<TkSettleBillRecord[]>([]);
+const formModel = ref(generateFormModel());
+const cloneColumns = ref<Column[]>([]);
+const showColumns = ref<Column[]>([]);
+
+const size = ref<SizeProps>('medium');
+
+const scrollbar = ref(true);
+const scroll = {
+    x: '100%',
+    y: '100%',
+    maxHeight: 'calc( 100vh - 420px)',
+};
+
+const basePagination: Pagination = {
+    current: 1,
+    pageSize: 20,
+};
+const pagination = reactive({
+    ...basePagination,
+});
+const densityList = computed(() => [
+    {
+        name: t('searchTable.size.mini'),
+        value: 'mini',
+    },
+    {
+        name: t('searchTable.size.small'),
+        value: 'small',
+    },
+    {
+        name: t('searchTable.size.medium'),
+        value: 'medium',
+    },
+    {
+        name: t('searchTable.size.large'),
+        value: 'large',
+    },
+]);
+
+const columns = computed<TableColumnData[]>(() => [
+    {
+        title: '日期',
+        dataIndex: 'xAxis',
+        slotName: 'xAxis',
+        fixed: 'left',
+    },
+    {
+        title: '状态',
+        dataIndex: 'settleStatus',
+        width: 120,
+    },
+
+    {
+        title: '结算总收入',
+        dataIndex: 'settleIncomeTotal',
+        width: 120,
+    },
+    {
+        title: '他方分成',
+        dataIndex: 'dividedIncome',
+        width: 120,
+    },
+    {
+        title: '平台服务费',
+        dataIndex: 'platformServiceFee',
+        width: 120,
+    },
+
+    {
+        title: '预计收益',
+        dataIndex: 'expectedEarnings',
+        width: 100,
+    },
+    {
+        title: '最后更新时间',
+        dataIndex: 'last_time',
+        width: 180,
+    },
+]);
+// queryTkSettleBills,
+// TkSettleBillTkSettleBillsParams,
+// TkSettleBillsParams,
+
+const fetchData = async (
+    params: TkSettleBillsParams = {
+        current: 1,
+        pageSize: 20,
+        sort: 'belongTime',
+        order: 'descending',
+    }
+) => {
+    setLoading(true);
+    try {
+        const { data } = await queryTkSettleBills(params);
+        console.log(data);
+        if (!data) return;
+        renderData.value = data.list;
+        pagination.current = data.page;
+        pagination.total = data.count;
+        console.log(data);
+    } catch (err) {
+        console.log(err);
+        // you can report use errorHandler or other
+    } finally {
+        setLoading(false);
+    }
+};
+
+const loadMore = async (
+    row: TableData,
+    done: (data: TableData[]) => void
+) => {
+    setLoading(true);
+    try {
+        let list: TableData[] = [];
+
+        if (row.accountName === '') {
+            const params: TkSettleBillsParams = {
+                query_date: [row.belongTime, row.belongTime],
+                current: 1,
+                pageSize: 999,
+                sort: 'belongTime',
+                order: 'descending',
+            };
+
+            const { data } = await queryTkSettleBills(params);
+            console.log(data);
+            if (!data) return;
+            list = data.list.map((item: TkSettleBillRecord) => ({
+                ...item,
+                isLeaf: false,
+            }));
+        } else {
+            const params: TkSettleBillsParams = {
+                query_date: [row.belongTime, row.belongTime],
+                accountName: row.accountName,
+                current: 1,
+                pageSize: 24,
+                sort: 'belongTime',
+                order: 'descending',
+            };
+
+            // const { data } = await queryTkSettleBillsHourtrend(params);
+            // console.log(data);
+            // if (!data) return;
+            // list = data.list.map((item: TkSettleBillRecord) => ({
+            //     ...item,
+            //     isLeaf: true,
+            // }));
+        }
+        done(list);
+    } catch (err) {
+        console.log(err);
+        // you can report use errorHandler or other
+    } finally {
+        setLoading(false);
+    }
+};
+
+const search = () => {
+    fetchData({
+        ...basePagination,
+        ...formModel.value,
+    } as unknown as TkSettleBillsParams);
+};
+const onPageChange = (current: number) => {
+    fetchData({ ...basePagination, current });
+};
+
+fetchData();
+const reset = () => {
+    formModel.value = generateFormModel();
+};
+
+const handleSelectDensity = (
+    val: string | number | Record<string, any> | undefined,
+    e: Event
+) => {
+    size.value = val as SizeProps;
+};
+
+const handleChange = (
+    checked: boolean | (string | boolean | number)[],
+    column: Column,
+    index: number
+) => {
+    if (!checked) {
+        cloneColumns.value = showColumns.value.filter(
+            (item) => item.dataIndex !== column.dataIndex
+        );
+    } else {
+        cloneColumns.value.splice(index, 0, column);
+    }
+};
+
+const exchangeArray = <T extends Array<any>>(
+    array: T,
+    beforeIdx: number,
+    newIdx: number,
+    isDeep = false
+): T => {
+    const newArray = isDeep ? cloneDeep(array) : array;
+    if (beforeIdx > -1 && newIdx > -1) {
+        // 先替换后面的,然后拿到替换的结果替换前面的
+        newArray.splice(
+            beforeIdx,
+            1,
+            newArray.splice(newIdx, 1, newArray[beforeIdx]).pop()
+        );
+    }
+    return newArray;
+};
+
+const popupVisibleChange = (val: boolean) => {
+    if (val) {
+        nextTick(() => {
+            const el = document.getElementById(
+                'tableSetting'
+            ) as HTMLElement;
+            const sortable = new Sortable(el, {
+                onEnd(e: any) {
+                    const { oldIndex, newIndex } = e;
+                    exchangeArray(cloneColumns.value, oldIndex, newIndex);
+                    exchangeArray(showColumns.value, oldIndex, newIndex);
+                },
+            });
+        });
+    }
+};
+
+watch(
+    () => columns.value,
+    (val) => {
+        cloneColumns.value = cloneDeep(val);
+        cloneColumns.value.forEach((item, index) => {
+            item.checked = true;
+        });
+        showColumns.value = cloneDeep(cloneColumns.value);
+    },
+    { deep: true, immediate: true }
+);
+</script>
+
+<script lang="ts">
+export default {
+    name: 'SearchTable',
+};
+</script>
+
+<style scoped lang="less">
+.container {
+    padding: 0 20px 20px 20px;
+}
+
+:deep(.arco-table-th) {
+    &:last-child {
+        .arco-table-th-item-title {
+            margin-left: 16px;
+        }
+    }
+}
+
+.action-icon {
+    margin-left: 12px;
+    cursor: pointer;
+}
+
+.active {
+    color: #0960bd;
+    background-color: #e3f4fc;
+}
+
+.setting {
+    display: flex;
+    align-items: center;
+    width: 200px;
+
+    .title {
+        margin-left: 12px;
+        cursor: pointer;
+    }
+}
+</style>

+ 4 - 15
src/views/taobao/report_daily.vue

@@ -1,20 +1,16 @@
 <template>
     <div class="container">
-        <Breadcrumb :items="['menu.dashboard', 'menu.dashboard.dailyLog']" />
-        <a-card class="general-card" :title="$t('menu.taobao.list')">
+        <Breadcrumb :items="['menu.dashboard', 'menu.taobao.report_daily']" />
+        <a-card class="general-card" :title="$t('menu.taobao.report_daily')">
             <a-row>
                 <a-col :flex="1">
                     <a-form :model="formModel" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
                         label-align="left">
                         <a-row :gutter="16">
-                            <a-col :span="8">
-                                <a-form-item field="name" label="账号名">
-                                    <a-input v-model="formModel.accountName" placeholder="输入关键词搜索" />
-                                </a-form-item>
-                            </a-col>
                             <a-col :span="8">
                                 <a-form-item field="query_date" :label="$t('searchTable.form.report_date')">
-                                    <a-range-picker v-model="formModel.query_date" style="width: 100%" />
+                                    <a-range-picker v-model="formModel.query_date" style="width: 100%"
+                                        @change="search" />
                                 </a-form-item>
                             </a-col>
                         </a-row>
@@ -94,13 +90,6 @@
             <a-table row-key="id" :loading="loading" :pagination="pagination"
                 :columns="(cloneColumns as TableColumnData[])" :scroll="scroll" :scrollbar="scrollbar"
                 :data="renderData" :bordered="false" :size="size" :load-more="loadMore" @page-change="onPageChange">
-                <template #index="{ rowIndex }">
-                    {{
-                        rowIndex +
-                        1 +
-                        (pagination.current - 1) * pagination.pageSize
-                    }}
-                </template>
                 <template #xAxis="{ record }">
                     <template v-if="record.accountName !== '' && !record.isLeaf">
                         {{ record.accountName }}

+ 55 - 53
src/views/taobao/updateCookies.vue

@@ -1,29 +1,31 @@
 <template>
-  <div class="container">
-    <Breadcrumb :items="['menu.taobao', 'menu.taobao.updateCookies']" />
-    <a-row class="wrapper">
-      <a-col :span="24">
-        <a-form ref="formRef" :model="formData" class="form" :label-col-props="{ span: 4 }"
-          :wrapper-col-props="{ span: 19 }">
-          <a-form-item field="cookie" :label="$t('taobao.cookies.label')" :rules="[{ required: true }]"
-            row-class="keep-margin">
-            <a-textarea v-model="formData.cookie" auto-size class="cookie"
-              :placeholder="$t('taobao.cookies.placeholder')" />
-          </a-form-item>
-          <a-form-item>
-            <a-space>
-              <a-button type="primary" @click="validate">
-                {{ $t('form.save') }}
-              </a-button>
-              <a-button type="secondary" @click="reset">
-                {{ $t('form.reset') }}
-              </a-button>
-            </a-space>
-          </a-form-item>
-        </a-form>
-      </a-col>
-    </a-row>
-  </div>
+    <div class="container">
+        <Breadcrumb :items="['menu.taobao', 'menu.taobao.updateCookies']" />
+        <a-card class="general-card" :title="$t('menu.taobao.updateCookies')">
+            <a-row class="wrapper">
+                <a-col :span="24">
+                    <a-form ref="formRef" :model="formData" class="form" :label-col-props="{ span: 4 }"
+                        :wrapper-col-props="{ span: 19 }">
+                        <a-form-item field="cookie" :label="$t('taobao.cookies.label')" :rules="[{ required: true }]"
+                            row-class="keep-margin">
+                            <a-textarea v-model="formData.cookie" auto-size class="cookie"
+                                :placeholder="$t('taobao.cookies.placeholder')" />
+                        </a-form-item>
+                        <a-form-item>
+                            <a-space>
+                                <a-button type="primary" @click="validate">
+                                    {{ $t('form.save') }}
+                                </a-button>
+                                <a-button type="secondary" @click="reset">
+                                    {{ $t('form.reset') }}
+                                </a-button>
+                            </a-space>
+                        </a-form-item>
+                    </a-form>
+                </a-col>
+            </a-row>
+        </a-card>
+    </div>
 </template>
 
 <script lang="ts" setup>
@@ -38,57 +40,57 @@ const router = useRouter();
 
 const formRef = ref<FormInstance>();
 const formData = ref<UpdateCookieModel>({
-  cookie: '',
+    cookie: '',
 });
 const validate = async () => {
-  const res = await formRef.value?.validate();
-  if (!res) {
-    try {
-      const { data } = await updateCookies(formData.value);
-      console.log(data, data.msg);
-      const msg = data.msg || '更新成功!';
-      if (data.success) {
-        Message.success(msg);
-        router.push('/taobao/list');
-      } else {
-        Message.error(data.msg);
-      }
+    const res = await formRef.value?.validate();
+    if (!res) {
+        try {
+            const { data } = await updateCookies(formData.value);
+            console.log(data, data.msg);
+            const msg = data.msg || '更新成功!';
+            if (data.success) {
+                Message.success(msg);
+                router.push('/taobao/list');
+            } else {
+                Message.error(data.msg);
+            }
 
 
-    } catch (err) {
-      console.error(err)
+        } catch (err) {
+            console.error(err)
+        }
     }
-  }
 };
 const reset = async () => {
-  await formRef.value?.resetFields();
+    await formRef.value?.resetFields();
 };
 </script>
 
 <style scoped lang="less">
 .container {
-  padding: 0 20px 20px 20px;
+    padding: 0 20px 20px 20px;
 }
 
 .wrapper {
-  padding: 20px 0 0 20px;
-  min-height: 580px;
-  background-color: var(--color-bg-2);
-  border-radius: 4px;
+    padding: 20px 0 0 20px;
+    min-height: 580px;
+    background-color: var(--color-bg-2);
+    border-radius: 4px;
 }
 
 :deep(.section-title) {
-  margin-top: 0;
-  margin-bottom: 16px;
-  font-size: 14px;
+    margin-top: 0;
+    margin-bottom: 16px;
+    font-size: 14px;
 }
 
 .form {
-  margin: 0 auto;
+    margin: 0 auto;
 }
 
 .form .cookie {
-  height: 100%;
-  min-height: 500px;
+    height: 100%;
+    min-height: 500px;
 }
 </style>