From 03fbf454453daf7b3a45064ea781cf4bdcc76832 Mon Sep 17 00:00:00 2001
From: cloudroam <cloudroam>
Date: 星期四, 26 六月 2025 15:00:32 +0800
Subject: [PATCH] add 景点管理
---
pages/film/filmset.vue | 124 +++++++++
pages/film/film-location.vue | 403 +++++++++++++++++++++++++++++++++
pages/goods/film-category-list.vue | 165 +++++++++++++
3 files changed, 685 insertions(+), 7 deletions(-)
diff --git a/pages/film/film-location.vue b/pages/film/film-location.vue
new file mode 100644
index 0000000..3ef7d38
--- /dev/null
+++ b/pages/film/film-location.vue
@@ -0,0 +1,403 @@
+<template>
+ <el-bus-crud ref="crud" v-bind="tableConfig"/>
+</template>
+
+<script>
+import dayjs from 'dayjs'
+import 'dayjs/locale/zh-cn'
+
+dayjs.locale('zh-cn')
+export default {
+ data() {
+ return {
+ tableConfig: {
+ url: 'flower/api/filmLocation/list',
+ newUrl: 'flower/api/filmLocation/new',
+ editUrl: 'flower/api/filmLocation/edit',
+ deleteUrl: 'flower/api/filmLocation/delete',
+ dialogNeedRequest: true,
+ persistSelection: true,
+ columns: [
+ {type: 'selection'},
+ {label: '拍摄地点名称', prop: 'locationName', minWidth: 120},
+ {label: '拍摄地点图片', prop: 'locationUrl', minWidth: 120},
+ {label: '省', prop: 'province', minWidth: 120},
+ {label: '市', prop: 'city', minWidth: 150},
+ {label: '区', prop: 'region'},
+ {label: '详细地址', prop: 'address', minWidth: 150},
+ {label: '纬度坐标', prop: 'gpsLat', minWidth: 150},
+ {label: '经度坐标', prop: 'gpsLng', minWidth: 300},
+ {label: '场景类型', prop: 'sceneType'},
+ {label: '经典画面描述', prop: 'classicScene', minWidth: 400},
+ {label: '开放参观', prop: 'isOpenVisitStr', minWidth: 200},
+ {label: '参观提示', prop: 'visitInfo', minWidth: 120},
+ {label: '地标性建筑描述', prop: 'landmarkDesc'},
+ {label: '停车场信息', prop: 'parkingInfo', minWidth: 80},
+ {label: '周边设施描述', prop: 'surroundingFacilities', minWidth: 80},
+ {label: 'ARURL', prop: 'arEntry', minWidth: 80},
+ {label: '打卡记录量', prop: 'checkinCount', minWidth: 80},
+ // { label: '游客实拍图', prop: 'visitorPhotos', minWidth: 80 },
+ {label: '景点热度', prop: 'locationWeight', minWidth: 80},
+ {label: '运营权重', prop: 'operationWeight', minWidth: 80},
+ {
+ label: '启用/禁用',
+ formatter: (row) => (
+ <el-switch
+ value={row.isEnabled}
+ onChange={this.onEnabledChange.bind(this, row)}
+ ></el-switch>
+ ),
+ minWidth: 120,
+ fixed: 'right',
+ },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [
+ {label: '拍摄地点名称', id: 'locationName', type: 'input'},
+ {
+ label: '启用/禁用',
+ id: 'isEnabled',
+ type: 'bus-select-dict',
+ default: '1',
+ el: {
+ code: 'USER_ENABLED_OR_DISABLED',
+ clearable: true,
+ style: 'width:100%',
+ },
+ },
+ ],
+ },
+ ],
+ form: [
+ {
+ label: '拍摄地点名称:',
+ id: 'locationName',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入拍摄地点名称',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '封面图片:',
+ id: 'locationUrl',
+ type: 'bus-upload',
+ el: {
+ listType: 'picture-card',
+ limitSize: 2,
+ limit: 1,
+ tipText: '大小不超过2M',
+ },
+ },
+ {
+ label: '省:',
+ id: 'province',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入省',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '市:',
+ id: 'city',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入市',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '区:',
+ id: 'region',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入区',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '详细地址:',
+ id: 'address',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输详细地址',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '纬度坐标:',
+ id: 'gpsLat',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入纬度坐标',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '经度坐标:',
+ id: 'gpsLng',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入经度坐标',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '场景类型:',
+ id: 'sceneType',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入场景类型',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '经典画面描述:',
+ id: 'classicScene',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入经典画面描述',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '开放参观:',
+ id: 'isOpenVisit',
+ type: 'bus-select-dict',
+ el: {
+ code: 'IS_VISITOR',
+ style: 'width:100%',
+ clearable: true,
+ },
+ rules: {
+ required: true,
+ message: '请选择参观类型',
+ },
+ },
+ {
+ label: '参观提示:',
+ id: 'visitInfo',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入参观提示',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '地标性建筑描述:',
+ id: 'landmarkDesc',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入地标性建筑描述',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '停车场信息:',
+ id: 'parkingInfo',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入停车场信息',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '周边设施描述:',
+ id: 'surroundingFacilities',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入周边设施描述',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: 'ARURL:',
+ id: 'arEntry',
+ type: 'input',
+ rules: {
+ required: false,
+ message: '请输入ARURL',
+ trigger: 'blur',
+ },
+ },
+ // {
+ // label: '取景地内容:',
+ // id: 'filmContent',
+ // component: 'base-editor',
+ // richText: true,
+ // rules: { required: true, message: '请输入取景地内容', trigger: 'blur' },
+ // },
+ {
+ label: '打卡记录量:',
+ id: 'checkinCount',
+ type: 'input',
+ rules: {
+ required: false,
+ message: '请输入打卡记录量',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '景点热度:',
+ id: 'locationWeight',
+ type: 'input',
+ rules: {
+ required: false,
+ message: '请输入景点热度',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '运营权重:',
+ id: 'operationWeight',
+ type: 'input',
+ rules: {
+ required: false,
+ message: '请输入运营权重',
+ trigger: 'blur',
+ },
+ },
+ ],
+ extraButtons: [
+ {
+ text: '清除热度',
+ atClick: async (row) => {
+ try {
+ await this.$elBusUtil.confirm(`确定要清除热度吗?`)
+ const {code} = await this.$elBusHttp.request(
+ 'flower/api/filmLocation/setDown',
+ {params: {id: row.id}}
+ )
+ if (code === 0) {
+ this.$message.success(`清除热度成功`)
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ headerButtons: [
+ {
+ text: '合并',
+ type: 'primary',
+ disabled: (selected) =>
+ selected.filter((item) => item.isEnabled)
+ .length === 0,
+ atClick: async (selected) => {
+ const selectedNotice = selected.filter(
+ (item) => item.isEnabled
+ )
+ try {
+ await this.$elBusUtil.confirm(
+ `确定要合并这${selectedNotice.length -1}个景点到第一个吗?`
+ )
+ const {code} = await this.$elBusHttp.request(
+ 'flower/api/filmLocation/merge/batch',
+ {
+ method: 'post',
+ data: {
+ ids: selected.map((item) => item.id),
+ },
+ }
+ )
+ if (code === 0) {
+ this.$message.success('操作成功')
+ this.$refs.crud.clearSelection()
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: '批量删除',
+ type: 'danger',
+ disabled: (selected) => selected.length === 0,
+ atClick: async (selected) => {
+ try {
+ await this.$elBusUtil.confirm(
+ `确定要批量删除这${selected.length}个片场内容吗?`
+ )
+ const {code} = await this.$elBusHttp.request(
+ 'flower/api/filmLocation/delete/batch',
+ {
+ method: 'post',
+ data: {
+ ids: selected.map((item) => item.id),
+ },
+ }
+ )
+ if (code === 0) {
+ this.$message.success('操作成功')
+ this.$refs.crud.clearSelection()
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '影视景点管理',
+ }
+ },
+ methods: {
+ onEnabledChange(row, e) {
+ const url = 'flower/api/filmLocation/isEnable'
+ const text = e ? '启用' : '禁用'
+ this.$elBusUtil
+ .confirm(`确定要${text}这个影视景点吗?`)
+ .then(async () => {
+ const { code } = await this.$elBusHttp.request(url, {
+ params: {
+ id: row.id,
+ },
+ })
+ if (code === 0) {
+ this.$message.success(`${text}成功`)
+ this.$refs.crud.getList()
+ }
+ })
+ .catch(() => { })
+ },
+
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+::v-deep {
+ .el-upload {
+ &-list__item {
+ width: 345px;
+ height: 128px;
+ }
+
+ &.el-upload--picture-card {
+ width: 345px;
+ height: 128px;
+ }
+ }
+}
+</style>
diff --git a/pages/content/filmset.vue b/pages/film/filmset.vue
similarity index 77%
rename from pages/content/filmset.vue
rename to pages/film/filmset.vue
index 30f9837..561cc69 100644
--- a/pages/content/filmset.vue
+++ b/pages/film/filmset.vue
@@ -189,9 +189,70 @@
limitSize: 2,
limit: 1,
tipText: '大小不超过2M',
- valueType: 'string',
+ // valueType: 'string',
},
forceDisabled: true,
+ inputFormat: (row) => {
+ if ('coverUrl' in row) {
+ if (typeof row.coverUrl === 'string' && row.coverUrl) {
+ let url = row.coverUrl
+ if (url.includes('doubanio')) {
+ url = `https://images.weserv.nl/?url=${encodeURIComponent(url)}`
+ }
+ return [{ url }]
+ }
+ if (Array.isArray(row.coverUrl)) {
+ return row.coverUrl
+ }
+ return []
+ }
+ },
+ outputFormat: (fileList) => {
+ if (Array.isArray(fileList) && fileList.length > 0) {
+ let url = fileList[0].url
+ const proxyPrefix = 'https://images.weserv.nl/?url='
+ if (url.startsWith(proxyPrefix)) {
+ url = decodeURIComponent(url.replace(proxyPrefix, ''))
+ }
+ return url
+ }
+ return ''
+ }
+ // outputFormat: (fileList) => {
+ // if (Array.isArray(fileList) && fileList.length > 0) {
+ // return fileList[0].url
+ // }
+ // return ''
+ // }
+ // inputFormat: (row) => {
+ // if ('coverUrl' in row) {
+ // // 封面图片是字符串
+ // if (typeof row.coverUrl === 'string' && row.coverUrl) {
+ // let url = row.coverUrl
+ // console.log("我进来了",url)
+ // // 代理 doubanio 图片
+ // if (url.includes('doubanio')) {
+ // url = `https://images.weserv.nl/?url=${encodeURIComponent(url)}`
+ // console.log("我处理了",url)
+ // }
+ // return [{ url }]
+ // }
+ // // 已经是数组
+ // if (Array.isArray(row.coverUrl)) {
+ // // 这里也可以做一遍代理处理,防止后端返回的数组里有 doubanio
+ // return row.coverUrl.map(item => {
+ // let url = item.url || item
+ // if (url.includes('doubanio')) {
+ // url = `https://images.weserv.nl/?url=${encodeURIComponent(url)}`
+ // }
+ // return { ...item, url }
+ // })
+ // }
+ // return []
+ // }
+ // },
+ // formatter: (url) => this.formatImageUrl(url)
+
},
{
label: '封面图片描述:',
@@ -316,6 +377,24 @@
await this.$elBusUtil.confirm(`确定要${action}吗?`)
const { code } = await this.$elBusHttp.request(
'flower/api/filmWorks/changeStatus',
+ { params: { id: row.id } }
+ )
+ if (code === 0) {
+ this.$message.success(`${action}成功`)
+ }
+ } catch (e) {
+ return false
+ }
+ },
+ },
+ {
+ text: (row) => ( row.status === 'pending_create' ? '取消生成' : '重新生成'),
+ atClick: async (row) => {
+ const action = row.status === 'pending_create' ? '取消生成' : '重新生成'
+ try {
+ await this.$elBusUtil.confirm(`确定要${action}吗?`)
+ const { code } = await this.$elBusHttp.request(
+ 'flower/api/filmWorks/changeCreateStatus',
{ params: { id: row.id } }
)
if (code === 0) {
@@ -495,13 +574,44 @@
}
},
methods: {
- formatterImage(row) {
- if (row.coverUrl) {
- // 使用第三方镜像服务(示例)
- const proxyUrl = `https://images.weserv.nl/?url=${encodeURIComponent(row.coverUrl)}`;
- return <el-bus-image src={proxyUrl} preview-src-list={[proxyUrl]} style="width:150px" />
+ // formatterImage(row) {
+ // if (row.coverUrl) {
+ // if (row.coverUrl.includes('doubanio')) {
+ // const proxyUrl = `https://images.weserv.nl/?url=${encodeURIComponent(row.coverUrl)}`;
+ // return <el-bus-image src={proxyUrl} preview-src-list={[proxyUrl]} style="width:150px" />
+ // } else {
+ // return <el-bus-image src={row.coverUrl} preview-src-list={[row.coverUrl]} style="width:150px" />
+ // }
+ // }
+ // return '无封面';
+ // }
+ formatImageUrl(url) {
+ if (!url) return '';
+ if (url.includes('doubanio')) {
+ return `https://images.weserv.nl/?url=${encodeURIComponent(url)}`;
}
- return '无封面';
+ return url;
+ },
+ // formatterImage(row) {
+ // if (!row.coverUrl) return '无封面';
+ // const displayUrl = this.formatImageUrl(row.coverUrl);
+ // return <el-bus-image src={displayUrl} preview-src-list={[displayUrl]} style="width:150px" />;
+ // }
+ formatterImage(row) {
+ if (!row.coverUrl) return '无封面';
+
+ // 统一处理路径:doubanio路径使用代理,其他直接使用
+ const displayUrl = row.coverUrl.includes('doubanio')
+ ? `https://images.weserv.nl/?url=${encodeURIComponent(row.coverUrl)}`
+ : row.coverUrl;
+
+ return (
+ <el-bus-image
+ src={displayUrl}
+ preview-src-list={[displayUrl]}
+ style="width:150px"
+ />
+ );
}
},
}
diff --git a/pages/goods/film-category-list.vue b/pages/goods/film-category-list.vue
new file mode 100644
index 0000000..9b374fe
--- /dev/null
+++ b/pages/goods/film-category-list.vue
@@ -0,0 +1,165 @@
+<template>
+ <el-bus-crud ref="crud" v-bind="tableConfig" />
+</template>
+
+<script>
+import cloneDeep from 'lodash.clonedeep'
+import { getSortConfig } from '@/utils/form-item-config'
+export default {
+ data() {
+ return {
+ originalList: [],
+ expandIds: [],
+ tableConfig: {
+ url: 'flower/api/film/category/tree',
+ hasPagination: false,
+ saveQuery: false,
+ isTree: true,
+ hasView: false,
+ canNewChild: (row) => !row.parentId,
+ dialogAttrs: {
+ width: '70%',
+ },
+ afterRequest: (list) => {
+ this.originalList = cloneDeep(list)
+ this.expandIds = this.expandIds.filter((i) =>
+ list.find((item) => item.id === i)
+ )
+ return list.map((i) => ({
+ ...i,
+ childrenCount: Array.isArray(i.children) ? i.children.length : 0,
+ children: this.expandIds.includes(i.id)
+ ? i.children
+ : i.children.slice(0, 1),
+ }))
+ },
+ tableEventHandlers: {
+ expandChange: (row, expand) => {
+ if (expand) {
+ if (!this.expandIds.includes(row.id)) {
+ this.expandIds.push(row.id)
+ }
+ row.children =
+ this.originalList.find((i) => i.id === row.id)?.children || []
+ } else {
+ const index = this.expandIds.indexOf(row.id)
+ if (index !== -1) {
+ this.expandIds.splice(index, 1)
+ }
+ }
+ },
+ },
+ beforeOpen(row, isNew) {
+ if (isNew && row.name) {
+ row.parentName = row.name
+ }
+ if (!isNew && !row.parentId && row.parentName) {
+ row.parentName = ''
+ }
+ },
+ extraParentKeys: ['parentName', 'levelLimit'],
+ tableAttrs: {
+ rowKey: 'id',
+ },
+ columns: [
+ { label: '板块名称', prop: 'name' },
+ {
+ label: '板块图片',
+ formatter: (row) => (
+ <el-bus-image
+ style="width:50px;height:50px"
+ lazy={true}
+ src={row.imageUrl}
+ />
+ ),
+ },
+ { label: '排序', prop: 'sortBy' },
+ { label: '子板块数量', formatter: (row) => row.childrenCount },
+ {
+ label: '是否显示',
+ formatter: (row) => (
+ <el-switch
+ value={row.shown}
+ onChange={this.onShownChange.bind(this, row)}
+ ></el-switch>
+ ),
+ },
+ ],
+ searchForm: [
+ {
+ type: 'row',
+ items: [{ label: '板块名称', id: 'name', type: 'input' }],
+ },
+ ],
+ form: [
+ {
+ label: '上级板块:',
+ id: 'parentName',
+ type: 'input',
+ readonly: true,
+ hidden: (row) => !row.parentName,
+ },
+ {
+ label: '板块名称:',
+ id: 'name',
+ type: 'input',
+ rules: {
+ required: true,
+ message: '请输入板块名称',
+ trigger: 'blur',
+ },
+ },
+ {
+ label: '板块图片:',
+ id: 'imageUrl',
+ type: 'bus-upload',
+ el: {
+ listType: 'picture-card',
+ limit: 1,
+ limitSize: 2,
+ tipText: '建议上传164*164的图片,大小不超过2M',
+ valueType: 'string',
+ },
+ rules: { required: false, message: '请上传板块图片' },
+ },
+ {
+ ...getSortConfig(),
+ },
+ ],
+ },
+ }
+ },
+ head() {
+ return {
+ title: '片场板块管理',
+ }
+ },
+ methods: {
+ onShownChange(row, e) {
+ const url = e
+ ? 'flower/api/film/category/tree/shown'
+ : 'film/api/film/category/tree/hidden'
+ const text = e ? '显示' : '隐藏'
+ this.$elBusUtil
+ .confirm(`确定要${text}这个板块吗?`)
+ .then(async () => {
+ const { code } = await this.$elBusHttp.request(url, {
+ params: {
+ id: row.id,
+ },
+ })
+ if (code === 0) {
+ this.$message.success(`${text}成功`)
+ this.$refs.crud.getList()
+ }
+ })
+ .catch(() => {})
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.category-list {
+}
+</style>
--
Gitblit v1.9.3