fix(stats/self_input): 修复保存不入库/路由 404,补全部门与自媒体来源下拉
- 模型去掉 defaultSoftDelete=0,避免 ThinkPHP 软删过滤导致新增记录"隐形" - 菜单 paths 改相对路径并新增「自录数据统计」目录,迁移已有菜单 - Lists/Overview 补部门关联、支持按部门筛选 - 数据权限改由 self_input_stats_view_all_roles 白名单 + DataScope 控制, 编辑/删除完全交给按钮权限校验(移除"仅本人可改"硬限制) - 新增 /stats.self_input/mediaSourceOptions 接口,统计页/账户消耗页/录入弹窗 共用 useMediaSourceOptions + MediaSourceSelect,去重动态加载 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -25,13 +25,32 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="自媒体来源">
|
||||
<el-input
|
||||
v-model="queryParams.media_source"
|
||||
placeholder="筛选自媒体来源"
|
||||
<el-form-item label="部门">
|
||||
<el-tree-select
|
||||
v-model="queryParams.dept_id"
|
||||
:data="deptOptions"
|
||||
node-key="id"
|
||||
:props="deptTreeProps"
|
||||
:default-expand-all="true"
|
||||
check-strictly
|
||||
placeholder="全部部门"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 220px"
|
||||
@keyup.enter="handleQuery"
|
||||
@change="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="自媒体来源">
|
||||
<media-source-select
|
||||
v-model="queryParams.media_source"
|
||||
:options="mediaSourceOptions"
|
||||
:loading="mediaSourceLoading"
|
||||
placeholder="全部来源"
|
||||
:allow-create="false"
|
||||
select-style="width: 220px"
|
||||
@change="handleQuery"
|
||||
@visible-change="onMediaSourceDropdownVisible"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
@@ -65,12 +84,13 @@
|
||||
</template>
|
||||
新增业绩
|
||||
</el-button>
|
||||
<router-link
|
||||
<el-button
|
||||
v-if="accountCostEntryPath"
|
||||
v-perms="['stats.personal_account_cost/lists']"
|
||||
to="/stats/self_input/account_cost"
|
||||
@click="goAccountCostEntry"
|
||||
>
|
||||
<el-button>账户消耗录入</el-button>
|
||||
</router-link>
|
||||
账户消耗录入
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -85,6 +105,12 @@
|
||||
<el-table-column label="日期" prop="yeji_date" min-width="110" fixed="left" />
|
||||
<el-table-column label="自媒体来源" prop="media_source" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="录入人" prop="creator_name" min-width="100" />
|
||||
<el-table-column label="部门" prop="dept_name" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.dept_name">{{ row.dept_name }}</span>
|
||||
<span v-else class="text-gray-400">未分配</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-for="col in tableColumns"
|
||||
:key="col.key"
|
||||
@@ -100,7 +126,6 @@
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="canMutateRow(row)"
|
||||
v-perms="['stats.personal_yeji/edit']"
|
||||
type="primary"
|
||||
link
|
||||
@@ -109,7 +134,6 @@
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="canMutateRow(row)"
|
||||
v-perms="['stats.personal_yeji/delete']"
|
||||
type="danger"
|
||||
link
|
||||
@@ -126,7 +150,13 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<yeji-edit-popup v-if="showYejiEdit" ref="yejiEditRef" @success="fetchOverview" @close="showYejiEdit = false" />
|
||||
<yeji-edit-popup
|
||||
v-if="showYejiEdit"
|
||||
ref="yejiEditRef"
|
||||
:media-source-options="mediaSourceOptions"
|
||||
@success="handleYejiSuccess"
|
||||
@close="showYejiEdit = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -135,11 +165,14 @@ import {
|
||||
getSelfInputOverview,
|
||||
personalYejiDelete,
|
||||
} from '@/api/self_input_stats'
|
||||
import { deptAll } from '@/api/org/department'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { getRoutePath } from '@/router'
|
||||
import feedback from '@/utils/feedback'
|
||||
|
||||
import MediaSourceSelect from './MediaSourceSelect.vue'
|
||||
import YejiEditPopup from './yeji-edit.vue'
|
||||
import { useMediaSourceOptions } from './useMediaSourceOptions'
|
||||
|
||||
type MetricType = 'count' | 'money' | 'percent' | 'ratio'
|
||||
|
||||
@@ -156,10 +189,23 @@ interface MetricColumn {
|
||||
minWidth?: number
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const yejiEditRef = shallowRef<InstanceType<typeof YejiEditPopup>>()
|
||||
const showYejiEdit = ref(false)
|
||||
const dateRange = ref<string[]>([])
|
||||
const deptOptions = ref<any[]>([])
|
||||
const {
|
||||
mediaSourceOptions,
|
||||
mediaSourceLoading,
|
||||
loadMediaSourceOptions,
|
||||
refreshMediaSourceOptions,
|
||||
} = useMediaSourceOptions()
|
||||
|
||||
const deptTreeProps = {
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
}
|
||||
|
||||
const overview = reactive<Record<string, any>>({
|
||||
date_range: [],
|
||||
@@ -168,11 +214,20 @@ const overview = reactive<Record<string, any>>({
|
||||
|
||||
const queryParams = reactive({
|
||||
media_source: '',
|
||||
dept_id: undefined as number | undefined,
|
||||
time_type: 'today',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
})
|
||||
|
||||
const accountCostEntryPath = computed(() => getRoutePath('stats.personal_account_cost/lists') || '')
|
||||
|
||||
const goAccountCostEntry = () => {
|
||||
if (accountCostEntryPath.value) {
|
||||
router.push(accountCostEntryPath.value)
|
||||
}
|
||||
}
|
||||
|
||||
const fetchOverviewList = async (params: Record<string, any>) => {
|
||||
if (queryParams.time_type === 'custom') {
|
||||
params.start_date = dateRange.value[0] || ''
|
||||
@@ -183,6 +238,33 @@ const fetchOverviewList = async (params: Record<string, any>) => {
|
||||
return res
|
||||
}
|
||||
|
||||
const loadDeptOptions = async () => {
|
||||
try {
|
||||
const res = await deptAll({ apply_data_scope: 1 })
|
||||
if (Array.isArray(res)) {
|
||||
deptOptions.value = res
|
||||
}
|
||||
} catch (e) {
|
||||
deptOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const onMediaSourceDropdownVisible = (visible: boolean) => {
|
||||
if (visible) {
|
||||
refreshMediaSourceOptions()
|
||||
}
|
||||
}
|
||||
|
||||
const handleYejiSuccess = async () => {
|
||||
await refreshMediaSourceOptions()
|
||||
await fetchOverview()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadDeptOptions()
|
||||
loadMediaSourceOptions()
|
||||
})
|
||||
|
||||
const { pager, getLists } = usePaging({
|
||||
fetchFun: fetchOverviewList,
|
||||
params: queryParams,
|
||||
@@ -234,14 +316,6 @@ const dateRangeText = computed(() => {
|
||||
return `${overview.date_range[0]} 至 ${overview.date_range[1]}`
|
||||
})
|
||||
|
||||
const currentAdminId = computed(() => Number(userStore.userInfo?.id || 0))
|
||||
const isRoot = computed(() => Number(userStore.userInfo?.root || 0) === 1)
|
||||
|
||||
const canMutateRow = (row: Record<string, any>) => {
|
||||
if (isRoot.value) return true
|
||||
return Number(row.creator_id) === currentAdminId.value
|
||||
}
|
||||
|
||||
const fetchOverview = async () => {
|
||||
try {
|
||||
await getLists()
|
||||
@@ -273,6 +347,7 @@ const handleQuery = () => {
|
||||
|
||||
const handleReset = () => {
|
||||
queryParams.media_source = ''
|
||||
queryParams.dept_id = undefined
|
||||
queryParams.time_type = 'today'
|
||||
dateRange.value = []
|
||||
pager.page = 1
|
||||
@@ -289,6 +364,7 @@ const renderMetric = (key: string, source: Record<string, any>, type = 'count')
|
||||
}
|
||||
|
||||
const handleAddYeji = async () => {
|
||||
await refreshMediaSourceOptions()
|
||||
showYejiEdit.value = true
|
||||
await nextTick()
|
||||
yejiEditRef.value?.open('add')
|
||||
|
||||
Reference in New Issue
Block a user