Files
zyt/admin/src/views/asset/resource/index.vue
T
2026-07-03 12:01:49 +08:00

327 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="asset-resource-container">
<!-- 搜索区域 -->
<el-card class="!border-none" shadow="never">
<el-form class="ls-form" :model="searchData" inline>
<el-form-item class="w-[280px]" label="标题">
<el-input
placeholder="请输入标题关键词"
v-model="searchData.title"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
<el-form-item label="上传时间">
<el-date-picker
v-model="searchData.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
clearable
style="width: 280px;"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 列表区域 -->
<el-card class="!border-none mt-4" shadow="never">
<div class="mb-4 flex items-center gap-2">
<el-button type="primary" @click="handleAdd">上传资源</el-button>
<el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">
批量删除
</el-button>
<span v-if="selectedIds.length > 0" class="text-sm text-gray-500">已选 {{ selectedIds.length }} </span>
</div>
<el-tabs v-model="queryParams.type" @tab-change="handleTabChange">
<el-tab-pane label="图片" name="1"></el-tab-pane>
<el-tab-pane label="视频" name="2"></el-tab-pane>
<el-tab-pane label="语音" name="3"></el-tab-pane>
</el-tabs>
<el-table :data="tableData" v-loading="loading" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<!-- <el-table-column prop="id" label="ID" width="80" /> -->
<el-table-column prop="title" label="标题" min-width="80" />
<el-table-column label="预览" width="100">
<template #default="{ row }">
<el-image v-if="row.type == 1" :src="row.file_url" style="width: 50px; height: 50px;" :preview-src-list="[row.file_url]" preview-teleported />
<el-link v-else :href="row.file_url" target="_blank" type="primary">点击预览</el-link>
</template>
</el-table-column>
<el-table-column label="关联账号" min-width="150" show-overflow-tooltip>
<template #default="{ row }">
<el-tag v-if="!row.users?.length" type="info" size="small">公开资源</el-tag>
<span v-else>{{ row.users?.map((u: any) => u.phone).join(', ') }}</span>
</template>
</el-table-column>
<el-table-column prop="create_time" label="上传时间" width="180" />
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-4 flex justify-end">
<el-pagination
v-model:current-page="queryParams.page_no"
v-model:page-size="queryParams.page_size"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="getList"
@current-change="getList"
/>
</div>
</el-card>
<!-- 新增/编辑资源弹窗 -->
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑资源' : '新增分发资源'" width="600px" destroy-on-close>
<el-form ref="formRef" :model="formData" :rules="computedRules" label-width="100px">
<el-form-item label="资源类型" prop="type" v-if="!isEdit">
<el-radio-group v-model="formData.type">
<el-radio :label="1">图片</el-radio>
<el-radio :label="2">视频</el-radio>
<el-radio :label="3">语音</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="formData.title" placeholder="请输入资源标题" />
</el-form-item>
<el-form-item label="资源文件" prop="file_url" v-if="!isEdit">
<div v-if="formData.type === 1" class="w-full">
<material-picker v-model="formData.file_url" :limit="9" type="image" />
<div style="font-size: 12px; color: #909399; margin-top: 4px;">支持批量上传多张图片一次最多 9 每张图片将生成一条独立资源多张时标题自动追加序号</div>
</div>
<upload v-else-if="formData.type === 2" type="video" direct :multiple="false" :limit="1" :show-progress="true" @success="handleUploadSuccess">
<el-button type="primary">上传视频 (OSS直传)</el-button>
</upload>
<upload v-else-if="formData.type === 3" type="voice" direct :multiple="false" :limit="1" :show-progress="true" @success="handleUploadSuccess">
<el-button type="primary">上传语音 (OSS直传)</el-button>
</upload>
<div v-if="formData.file_url && formData.type !== 1" class="ml-4 text-primary truncate max-w-xs" :title="formData.file_url">
已上传: {{ formData.file_url }}
</div>
</el-form-item>
<el-form-item label="关联账号" prop="user_ids">
<el-select v-model="formData.user_ids" multiple filterable placeholder="不选择则为公开资源" clearable style="width: 100%;">
<el-option v-for="item in userOptions" :key="item.id" :label="item.phone" :value="item.id" />
</el-select>
<div style="font-size: 12px; color: #909399; margin-top: 4px;">不选择账号则资源为公开资源所有人可见</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm" :loading="submitLoading">{{ isEdit ? '保存修改' : '确定发布' }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { apiAssetResourceList, apiAssetResourceAdd, apiAssetResourceEdit, apiAssetResourceDelete, apiAssetUserList } from '@/api/asset'
import MaterialPicker from '@/components/material/picker.vue'
import Upload from '@/components/upload/index.vue'
const loading = ref(false)
const tableData = ref<any[]>([])
const total = ref(0)
const selectedIds = ref<number[]>([])
const searchData = reactive<any>({
title: '',
dateRange: []
})
const queryParams = reactive<any>({
type: '1',
page_no: 1,
page_size: 15
})
const dialogVisible = ref(false)
const submitLoading = ref(false)
const formRef = ref()
const isEdit = ref(false)
const editId = ref(0)
const userOptions = ref<any[]>([])
const formData = reactive<any>({
type: 1,
title: '',
file_url: '',
cover_url: '',
user_ids: []
})
const validateFileUrl = (_rule: any, value: any, callback: any) => {
if (Array.isArray(value)) {
if (!value.length) return callback(new Error('请上传资源文件'))
} else if (!value) {
return callback(new Error('请输入或上传文件获取链接'))
}
callback()
}
const computedRules = computed(() => {
if (isEdit.value) {
return {
title: [{ required: true, message: '请输入标题', trigger: 'blur' }]
}
}
return {
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
file_url: [{ required: true, validator: validateFileUrl, trigger: 'change' }]
}
})
const buildQueryParams = () => {
const params: any = { ...queryParams }
if (searchData.title) {
params.title = searchData.title
}
if (searchData.dateRange && searchData.dateRange.length === 2) {
params.start_time = searchData.dateRange[0]
params.end_time = searchData.dateRange[1]
}
return params
}
const getList = async () => {
loading.value = true
try {
const res = await apiAssetResourceList(buildQueryParams())
tableData.value = res.lists
total.value = res.count
selectedIds.value = []
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
const fetchUserOptions = async () => {
try {
const res = await apiAssetUserList({ page_no: 1, page_size: 9999 })
userOptions.value = res.lists
} catch (e) {
console.error(e)
}
}
const handleSearch = () => {
queryParams.page_no = 1
getList()
}
const handleReset = () => {
searchData.title = ''
searchData.dateRange = []
queryParams.page_no = 1
getList()
}
const handleTabChange = () => {
queryParams.page_no = 1
getList()
}
const handleAdd = () => {
isEdit.value = false
editId.value = 0
Object.assign(formData, { type: parseInt(queryParams.type), title: '', file_url: '', cover_url: '', user_ids: [] })
fetchUserOptions()
dialogVisible.value = true
}
const handleEdit = (row: any) => {
isEdit.value = true
editId.value = row.id
Object.assign(formData, {
type: row.type,
title: row.title,
file_url: row.file_url,
cover_url: row.cover_url || '',
user_ids: row.users?.map((u: any) => u.id) || []
})
fetchUserOptions()
dialogVisible.value = true
}
const handleUploadSuccess = (response: any) => {
formData.file_url = response?.data?.uri || response?.data?.url || ''
ElMessage.success('上传成功')
}
const handleDelete = async (row: any) => {
try {
await ElMessageBox.confirm('确定要删除该资源吗?用户将无法再看到。', '提示', { type: 'warning' })
await apiAssetResourceDelete({ id: row.id })
ElMessage.success('删除成功')
getList()
} catch (e) {
// canceled
}
}
const handleSelectionChange = (rows: any[]) => {
selectedIds.value = rows.map((row) => Number(row.id)).filter((id) => id > 0)
}
const handleBatchDelete = async () => {
if (!selectedIds.value.length) return
try {
await ElMessageBox.confirm(`确定要删除选中的 ${selectedIds.value.length} 个资源吗?用户将无法再看到。`, '提示', { type: 'warning' })
await apiAssetResourceDelete({ id: selectedIds.value })
ElMessage.success('删除成功')
getList()
} catch (e) {
// canceled
}
}
const submitForm = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid: boolean) => {
if (valid) {
submitLoading.value = true
try {
if (isEdit.value) {
await apiAssetResourceEdit({
id: editId.value,
title: formData.title,
user_ids: formData.user_ids
})
ElMessage.success('编辑成功')
} else {
await apiAssetResourceAdd(formData)
ElMessage.success('上传分发成功')
}
dialogVisible.value = false
getList()
} catch (e) {
console.error(e)
} finally {
submitLoading.value = false
}
}
})
}
onMounted(() => {
getList()
})
</script>