feat: 数据看板
This commit is contained in:
@ -0,0 +1,131 @@
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-27 18:08:04
|
||||
-->
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="action-row mb-12px flex justify-between">
|
||||
<a-checkbox :model-value="checkedAll" :indeterminate="indeterminate" @change="handleChangeAll" />
|
||||
<div class="flex items-center">
|
||||
<a-button class="w-110px search-btn mr-12px" size="medium">
|
||||
<template #icon> <icon-download /> </template>
|
||||
<template #default>导出数据</template>
|
||||
</a-button>
|
||||
<a-button class="w-110px search-btn" size="medium">
|
||||
<template #icon>
|
||||
<img :src="icon1" width="14" height="14" />
|
||||
</template>
|
||||
<template #default>自定义列</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="dataSource"
|
||||
row-key="id"
|
||||
:row-selection="rowSelection"
|
||||
:pagination="false"
|
||||
class="custom-table w-100%"
|
||||
bordered
|
||||
>
|
||||
<template #columns>
|
||||
<a-table-column title="账号名称" data-index="name" />
|
||||
<a-table-column title="手机号" data-index="mobile" />
|
||||
<a-table-column title="账号ID" data-index="account_id" />
|
||||
<a-table-column title="持有人" data-index="holder_name" />
|
||||
<a-table-column title="平台" data-index="platform">
|
||||
<template #cell="{ record }">
|
||||
{{ record.platform === 0 ? '抖音' : record.platform === 1 ? '小红书' : '-' }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="分组" data-index="group.name" />
|
||||
<a-table-column title="运营人员" data-index="operator.name" />
|
||||
<a-table-column title="粉丝数" data-index="fans_number" />
|
||||
<a-table-column title="点赞数" data-index="like_number" />
|
||||
<a-table-column title="收藏数" data-index="collect_number" />
|
||||
<a-table-column title="观看数" data-index="view_number" />
|
||||
<a-table-column title="观看环比" data-index="view_chain">
|
||||
<template #cell="{ record }"> {{ record.view_chain }}% </template>
|
||||
</a-table-column>
|
||||
<a-table-column title="点赞环比" data-index="like_chain">
|
||||
<template #cell="{ record }"> {{ record.like_chain }}% </template>
|
||||
</a-table-column>
|
||||
<a-table-column title="状态" data-index="status">
|
||||
<template #cell="{ record }">
|
||||
<span v-if="record.status === 0">未授权</span>
|
||||
<span v-else-if="record.status === 1">正常</span>
|
||||
<span v-else-if="record.status === 2">异常</span>
|
||||
<span v-else>-</span>
|
||||
<span v-if="record.is_pause === 1" class="pause-tag">(暂停)</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="操作" fixed="right" width="120">
|
||||
<template #cell="{ record }">
|
||||
<a-button type="text" size="small" @click="handleEdit(record)">详情</a-button>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import icon1 from '@/assets/img/media-account/icon-custom.png';
|
||||
|
||||
const props = defineProps({
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['edit', 'delete', 'selectionChange']);
|
||||
|
||||
const selectedItems = ref([]);
|
||||
|
||||
const checkedAll = computed(
|
||||
() => selectedItems.value.length > 0 && selectedItems.value.length === props.dataSource.length,
|
||||
);
|
||||
const indeterminate = computed(
|
||||
() => selectedItems.value.length > 0 && selectedItems.value.length < props.dataSource.length,
|
||||
);
|
||||
|
||||
const rowSelection = {
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: selectedItems.value,
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedItems.value = selectedRowKeys;
|
||||
emit('selectionChange', selectedRows);
|
||||
},
|
||||
};
|
||||
|
||||
const handleChangeAll = (checked) => {
|
||||
if (checked) {
|
||||
selectedItems.value = props.dataSource.map((item) => item.id);
|
||||
} else {
|
||||
selectedItems.value = [];
|
||||
}
|
||||
emit('selectionChange', checked ? props.dataSource : []);
|
||||
};
|
||||
|
||||
const handleEdit = (record) => {
|
||||
emit('edit', record);
|
||||
};
|
||||
|
||||
const handleDelete = (record) => {
|
||||
emit('delete', record);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './style.scss';
|
||||
|
||||
.custom-table {
|
||||
:deep(.pause-tag) {
|
||||
color: #f64b31;
|
||||
margin-left: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,10 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
.action-row {
|
||||
:deep(.arco-btn) {
|
||||
.arco-btn-icon {
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
<!-- eslint-disable vue/no-mutating-props -->
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 14:02:40
|
||||
-->
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="filter-row flex mb-20px">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">账号名称</span>
|
||||
<a-space size="medium" class="w-240px">
|
||||
<a-input v-model="query.name" placeholder="请搜索..." size="medium" allow-clear @change="handleSearch">
|
||||
<template #prefix>
|
||||
<icon-search />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">分组</span>
|
||||
<a-space class="w-200px">
|
||||
<group-select v-model="query.group_ids" multiple :options="groups" @change="handleSearch" />
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">状态</span>
|
||||
<a-space class="w-180px">
|
||||
<a-select v-model="query.status" size="medium" placeholder="全部" allow-clear @change="handleSearch">
|
||||
<a-option v-for="(item, index) in STATUS_LIST" :key="index" :value="item.value" :label="item.text">{{
|
||||
item.text
|
||||
}}</a-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">运营人员</span>
|
||||
<a-space class="w-160px">
|
||||
<a-select v-model="query.operator_id" size="medium" placeholder="全部" allow-clear @change="handleSearch">
|
||||
<a-option v-for="(item, index) in operators" :key="index" :value="item.id" :label="item.name">{{
|
||||
item.name
|
||||
}}</a-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-row flex">
|
||||
<div class="filter-row-item flex items-center">
|
||||
<span class="label">时间筛选</span>
|
||||
<a-space class="w-240px">
|
||||
<a-select
|
||||
v-model="query.date_range"
|
||||
size="medium"
|
||||
placeholder="全部"
|
||||
class="w-120px"
|
||||
allow-clear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #arrow-icon> <icon-calendar size="16" /> </template>
|
||||
<a-option :value="7" label="近7天">近7天</a-option>
|
||||
<a-option :value="14" label="近14天">近14天</a-option>
|
||||
<a-option :value="30" label="近30天">近30天</a-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-button class="w-84px search-btn mr-12px" size="medium" @click="handleSearch">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
<template #default>搜索</template>
|
||||
</a-button>
|
||||
<a-button class="w-84px reset-btn" size="medium" @click="handleReset">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
<template #default>重置</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, defineEmits, defineProps } from 'vue';
|
||||
import { fetchAccountGroups, fetchAccountOperators } from '@/api/all/propertyMarketing';
|
||||
import GroupSelect from '../group-select/index.vue';
|
||||
import { STATUS_LIST } from '@/views/property-marketing/media-account/account-dashboard/constants';
|
||||
|
||||
const props = defineProps({
|
||||
query: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits('onSearch', 'onReset', 'update:query');
|
||||
|
||||
const tags = ref([]);
|
||||
const groups = ref([]);
|
||||
const operators = ref([]);
|
||||
|
||||
const handleSearch = () => {
|
||||
emits('onSearch', props.query);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emits('onReset');
|
||||
};
|
||||
|
||||
const getGroups = async () => {
|
||||
const { code, data } = await fetchAccountGroups();
|
||||
if (code === 200) {
|
||||
groups.value = data;
|
||||
}
|
||||
};
|
||||
const getOperators = async () => {
|
||||
const { code, data } = await fetchAccountOperators();
|
||||
if (code === 200) {
|
||||
operators.value = data;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// getGroups();
|
||||
// getOperators();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './style.scss';
|
||||
</style>
|
||||
@ -0,0 +1,36 @@
|
||||
.container {
|
||||
:deep(.arco-input-wrapper),
|
||||
:deep(.arco-select-view-single),
|
||||
:deep(.arco-select-view-multiple) {
|
||||
border-radius: 4px;
|
||||
border-color: #d7d7d9;
|
||||
background-color: #fff;
|
||||
&:focus-within,
|
||||
&.arco-input-focus {
|
||||
background-color: var(--color-bg-2);
|
||||
border-color: rgb(var(--primary-6));
|
||||
box-shadow: 0 0 0 0 var(--color-primary-light-2);
|
||||
}
|
||||
}
|
||||
.filter-row {
|
||||
.filter-row-item {
|
||||
&:not(:last-child) {
|
||||
margin-right: 24px;
|
||||
}
|
||||
.label {
|
||||
margin-right: 8px;
|
||||
color: #211f24;
|
||||
font-family: 'Alibaba PuHuiTi';
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
flex-shrink: 0;
|
||||
line-height: 22px; /* 157.143% */
|
||||
}
|
||||
:deep(.arco-space-item) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
<!--
|
||||
* @Author: RenXiaoDong
|
||||
* @Date: 2025-06-25 14:02:40
|
||||
-->
|
||||
<template>
|
||||
<a-select
|
||||
v-model="selectedGroups"
|
||||
:multiple="multiple"
|
||||
size="medium"
|
||||
:placeholder="placeholder"
|
||||
allow-clear
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-option v-for="(item, index) in options" :key="index" :value="item.id" :label="item.name">
|
||||
{{ item.name }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Array, String, Number],
|
||||
default: () => [],
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '全部',
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
const selectedGroups = ref(props.multiple ? [] : '');
|
||||
|
||||
// 监听外部传入的值变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
selectedGroups.value = newVal;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 监听内部值变化,向外部发送更新
|
||||
watch(selectedGroups, (newVal) => {
|
||||
emits('update:modelValue', newVal);
|
||||
});
|
||||
|
||||
const handleChange = (value) => {
|
||||
selectedGroups.value = value;
|
||||
emits('change', value);
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user