Merge branch '20260518-long'
This commit is contained in:
@@ -22,16 +22,26 @@
|
||||
<el-select
|
||||
v-model="formData.media_channel_code"
|
||||
placeholder="请选择自媒体渠道"
|
||||
class="w-full"
|
||||
class="account-cost-channel-select w-full"
|
||||
filterable
|
||||
:disabled="mode === 'edit'"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in mediaChannelOptions"
|
||||
:key="item.code"
|
||||
:label="item.name"
|
||||
:value="item.code"
|
||||
/>
|
||||
<el-option-group
|
||||
v-for="g in mediaChannelGroups"
|
||||
:key="g.group_name"
|
||||
:label="g.group_name"
|
||||
>
|
||||
<el-option
|
||||
v-for="ch in g.channels"
|
||||
:key="ch.channel_code"
|
||||
:label="ch.channel_name"
|
||||
:value="ch.channel_code"
|
||||
>
|
||||
<div class="channel-opt-row">
|
||||
<span class="opt-name">{{ ch.channel_name }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门" prop="dept_id">
|
||||
@@ -78,9 +88,9 @@ import type { FormInstance } from 'element-plus'
|
||||
import { accountCostAdd, accountCostDetail, accountCostEdit } from '@/api/finance'
|
||||
import Popup from '@/components/popup/index.vue'
|
||||
|
||||
interface MediaChannelOption {
|
||||
code: string
|
||||
name: string
|
||||
interface MediaChannelGroup {
|
||||
group_name: string
|
||||
channels: { channel_code: string; channel_name: string }[]
|
||||
}
|
||||
|
||||
interface DeptOption {
|
||||
@@ -90,7 +100,7 @@ interface DeptOption {
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
mediaChannelOptions: MediaChannelOption[]
|
||||
mediaChannelGroups: MediaChannelGroup[]
|
||||
deptOptions: DeptOption[]
|
||||
defaultMediaChannelCode: string
|
||||
}>()
|
||||
@@ -197,3 +207,21 @@ defineExpose({
|
||||
getDetail,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.account-cost-channel-select {
|
||||
:deep(.channel-opt-row) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding-right: 4px;
|
||||
}
|
||||
:deep(.opt-name) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -27,14 +27,25 @@
|
||||
placeholder="筛选全部渠道"
|
||||
clearable
|
||||
filterable
|
||||
class="w-[220px]"
|
||||
class="account-cost-channel-select w-[220px]"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in mediaChannelOptions"
|
||||
:key="item.code"
|
||||
:label="item.name"
|
||||
:value="item.code"
|
||||
/>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option-group
|
||||
v-for="g in mediaChannelGroups"
|
||||
:key="g.group_name"
|
||||
:label="g.group_name"
|
||||
>
|
||||
<el-option
|
||||
v-for="ch in g.channels"
|
||||
:key="ch.channel_code"
|
||||
:label="ch.channel_name"
|
||||
:value="ch.channel_code"
|
||||
>
|
||||
<div class="channel-opt-row">
|
||||
<span class="opt-name">{{ ch.channel_name }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门">
|
||||
@@ -117,7 +128,7 @@
|
||||
<edit-popup
|
||||
v-if="showEdit"
|
||||
ref="editRef"
|
||||
:media-channel-options="mediaChannelOptions"
|
||||
:media-channel-groups="mediaChannelGroups"
|
||||
:dept-options="deptOptions"
|
||||
:default-media-channel-code="defaultMediaChannelCode"
|
||||
@success="getLists"
|
||||
@@ -133,6 +144,11 @@ import feedback from '@/utils/feedback'
|
||||
|
||||
import EditPopup from './edit.vue'
|
||||
|
||||
interface MediaChannelGroup {
|
||||
group_name: string
|
||||
channels: { channel_code: string; channel_name: string }[]
|
||||
}
|
||||
|
||||
interface MediaChannelOption {
|
||||
code: string
|
||||
name: string
|
||||
@@ -160,7 +176,7 @@ const { pager, getLists, resetPage } = usePaging({
|
||||
params: queryParams,
|
||||
})
|
||||
|
||||
const mediaChannelOptions = computed<MediaChannelOption[]>(() => {
|
||||
const mediaChannelOptionsFlat = computed<MediaChannelOption[]>(() => {
|
||||
const options = pager.extend.media_channel_options
|
||||
if (!Array.isArray(options)) return []
|
||||
|
||||
@@ -170,6 +186,26 @@ const mediaChannelOptions = computed<MediaChannelOption[]>(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
const mediaChannelGroups = computed<MediaChannelGroup[]>(() => {
|
||||
const groups = pager.extend.media_channel_groups
|
||||
if (Array.isArray(groups) && groups.length > 0) {
|
||||
return groups as MediaChannelGroup[]
|
||||
}
|
||||
const flat = mediaChannelOptionsFlat.value
|
||||
if (!flat.length) {
|
||||
return []
|
||||
}
|
||||
return [
|
||||
{
|
||||
group_name: '渠道',
|
||||
channels: flat.map(item => ({
|
||||
channel_code: item.code,
|
||||
channel_name: item.name,
|
||||
})),
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const deptOptions = computed<DeptOption[]>(() => {
|
||||
const options = pager.extend.dept_options
|
||||
return Array.isArray(options) ? options : []
|
||||
@@ -213,3 +249,21 @@ const handleReset = () => {
|
||||
|
||||
getLists()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.account-cost-channel-select {
|
||||
:deep(.channel-opt-row) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding-right: 4px;
|
||||
}
|
||||
:deep(.opt-name) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -68,6 +68,7 @@ class AccountCostLists extends BaseAdminDataLists implements ListsSearchInterfac
|
||||
'total_amount' => round((float) $this->baseQuery()->sum('amount'), 2),
|
||||
'days_count' => (int) $this->baseQuery()->distinct(true)->count('cost_date'),
|
||||
'media_channel_options' => MediaChannelService::getOptions(),
|
||||
'media_channel_groups' => MediaChannelService::getOptionGroups(),
|
||||
'dept_options' => DeptLogic::getAllData(),
|
||||
'supports_dept_binding' => AccountCost::supportsDeptBinding(),
|
||||
'default_media_channel_code' => MediaChannelService::getDefaultCode(),
|
||||
|
||||
@@ -11,6 +11,50 @@ use think\db\Query;
|
||||
|
||||
class MediaChannelService
|
||||
{
|
||||
/**
|
||||
* 与业绩看板「渠道来源」相同的分组(按 source_group_name),不含客户数统计。
|
||||
*
|
||||
* @return array<int, array{
|
||||
* group_name: string,
|
||||
* channels: array<int, array{channel_code: string, channel_name: string}>
|
||||
* }>
|
||||
*/
|
||||
public static function getOptionGroups(): array
|
||||
{
|
||||
$rows = QywxMediaChannel::where('status', 1)
|
||||
->field('channel_code, channel_name, source_group_name')
|
||||
->order('source_group_name asc, id asc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
if ($rows === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$groups = [];
|
||||
foreach ($rows as $r) {
|
||||
$g = (string) (($r['source_group_name'] ?? '') !== '' ? $r['source_group_name'] : '其它');
|
||||
$groups[$g] ??= ['group_name' => $g, 'channels' => []];
|
||||
$groups[$g]['channels'][] = [
|
||||
'channel_code' => (string) ($r['channel_code'] ?? ''),
|
||||
'channel_name' => (string) ($r['channel_name'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
$sortedGroups = array_values($groups);
|
||||
usort($sortedGroups, static function (array $a, array $b): int {
|
||||
$aMedia = mb_strpos($a['group_name'], '自媒体') !== false ? 0 : 1;
|
||||
$bMedia = mb_strpos($b['group_name'], '自媒体') !== false ? 0 : 1;
|
||||
if ($aMedia !== $bMedia) {
|
||||
return $aMedia <=> $bMedia;
|
||||
}
|
||||
|
||||
return strcmp($a['group_name'], $b['group_name']);
|
||||
});
|
||||
|
||||
return $sortedGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{code: string, name: string}>
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user