Lufaneng 1 year ago
parent 2d9fa1d234
commit 4f8d3c1bd0

@ -22,13 +22,13 @@ declare module 'vue' {
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
@ -36,23 +36,11 @@ declare module 'vue' {
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElUpload: typeof import('element-plus/es')['ElUpload']
IEpHelpFilled: typeof import('~icons/ep/help-filled')['default']
IEpHomeFilled: typeof import('~icons/ep/home-filled')['default']
IEpMenu: typeof import('~icons/ep/menu')['default']
IEpMessageBox: typeof import('~icons/ep/message-box')['default']
IEpPlus: typeof import('~icons/ep/plus')['default']
IEpScaleToOriginal: typeof import('~icons/ep/scale-to-original')['default']
MapSelector: typeof import('./components/MapSelector.vue')['default']
MapSelectorFinal: typeof import('./components/MapSelectorFinal.vue')['default']
MapSelectorFixed: typeof import('./components/MapSelectorFixed.vue')['default']
MapSelectorOptimized: typeof import('./components/MapSelectorOptimized.vue')['default']
MapSelectorSimple: typeof import('./components/MapSelectorSimple.vue')['default']
MapSelectorWithMarker: typeof import('./components/MapSelectorWithMarker.vue')['default']
MapTest: typeof import('./components/MapTest.vue')['default']
Pagination: typeof import('./components/Pagination/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SimpleMapTest: typeof import('./components/SimpleMapTest.vue')['default']
UploadFile: typeof import('./components/UploadFile.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']

@ -1,89 +0,0 @@
# 腾讯地图坐标选择器使用说明 (基于 tlbs-map-vue)
## 功能说明
MapSelector 是一个基于 `tlbs-map-vue` 的坐标选择器组件,用于在地图上选择经纬度坐标。
## 技术栈
- Vue 3
- tlbs-map-vue (腾讯地图官方 Vue 组件库)
- Element Plus
## 安装依赖
```bash
npm install tlbs-map-vue
```
## 使用方法
### 1. 配置腾讯地图API密钥
`src/config/map.js` 文件中,将 `TENCENT_MAP_KEY` 替换为你的实际API密钥
```javascript
export const MAP_CONFIG = {
TENCENT_MAP_KEY: '你的腾讯地图API密钥',
// ...其他配置
}
```
### 2. 获取腾讯地图API密钥
1. 访问腾讯位置服务官网https://lbs.qq.com/
2. 注册并登录账号
3. 创建应用并获取API密钥WebServiceAPI密钥
4. 在控制台中配置域名白名单
### 3. 在组件中使用
```vue
<template>
<MapSelector
v-model="showMapSelector"
:longitude="formField.longitude"
:latitude="formField.latitude"
@confirm="handleCoordinateConfirm"
/>
</template>
<script setup>
import MapSelector from '@/components/MapSelector.vue'
const showMapSelector = ref(false)
const formField = ref({
longitude: null,
latitude: null
})
const handleCoordinateConfirm = (coordinates) => {
formField.value.longitude = coordinates.longitude
formField.value.latitude = coordinates.latitude
}
</script>
```
## Props
- `modelValue`: Boolean - 控制对话框显示/隐藏
- `longitude`: Number - 初始经度值
- `latitude`: Number - 初始纬度值
## Events
- `update:modelValue`: 更新对话框显示状态
- `confirm`: 确认选择坐标时触发,返回 `{longitude, latitude}` 对象
## 特性
- ✅ 基于腾讯地图官方 Vue 组件库
- ✅ 支持点击地图选择坐标
- ✅ 实时显示选择的经纬度
- ✅ 支持初始坐标显示
- ✅ 响应式设计
- ✅ 自动处理地图加载
## 注意事项
1. 确保已正确配置腾讯地图API密钥
2. 需要在腾讯地图控制台中配置域名白名单
3. 在生产环境中建议将API密钥配置在环境变量中
4. 使用的是 WebServiceAPI 密钥,不是 JavaScript API 密钥
## 故障排除
如果地图无法显示:
1. 检查 API 密钥是否正确
2. 确认域名已添加到白名单
3. 检查网络连接
4. 查看浏览器控制台错误信息

@ -0,0 +1,67 @@
<template>
<div>
<el-upload
ref="uploadRef"
:action="`${upload.url}`"
:disabled="upload.isUploading"
drag
:headers="upload.headers"
:limit="1"
:accept="accept"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
>
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center"></div>
</template>
</el-upload>
</div>
</template>
<script setup>
import { useUserStore } from '@/stores/user'
const props = defineProps({
accept: {
type: String,
default: 'image/*',
},
})
const uploadRef = ref(null)
const emit = defineEmits(['onSuccess'])
/** * 用户导入参数 */
const upload = reactive({
//
open: false,
//
title: '',
//
isUploading: false,
//
headers: { session: `${useUserStore().token}` },
//
url: `/filestransfer/upload`,
})
/** 文件上传中处理 */
function handleFileUploadProgress(event, file, fileList) {
upload.isUploading = true
}
/** 文件上传成功处理 */
function handleFileSuccess(response, file, fileList) {
upload.open = false
upload.isUploading = false
uploadRef.value.handleRemove(file)
console.log(response)
if (response.status === 200) {
emit('onSuccess', response.result)
}
}
</script>
<style lang="scss" scoped></style>

@ -1,7 +1,7 @@
// 地图配置
export const MAP_CONFIG = {
// 腾讯地图API密钥 - 请替换为你的实际密钥
TENCENT_MAP_KEY: 'HGWBZ-QUB6Z-C7KXZ-TJRCH-IFLLO-AMFIJ',
TENCENT_MAP_KEY: 'N6LBZ-JLDCT-SQNXQ-LQUWR-6PQX7-TQBQN',
// 默认地图中心点(北京)
DEFAULT_CENTER: {

@ -1,67 +1,103 @@
<template>
<div class="aside">
<div class="scrollbar">
<el-scrollbar>
<el-menu :default-openeds="['/SystemManage']" :default-active="route.path" active-text-color="#1890FF" background-color="#fff" text-color="#000" router>
<template v-for="(item, index) in routes[0].children" :key="index">
<el-sub-menu :index="'/' + item.path" v-if="item.children && item.children.filter(el => !el.meta.hide).length > 0">
<template #title>
<span> {{ item.meta.title }}</span>
</template>
<el-menu-item v-for="(ele, idx) in item.children.filter(el => !el.meta.hide)" :index="'/' + item.path + '/' + ele.path" :key="idx">{{ ele.meta.title }}</el-menu-item>
</el-sub-menu>
<el-menu-item :index="item.redirect || '/' + item.path" v-else-if="!item.meta.hide">
<el-icon v-if="item.meta.title === '工作台'">
<Calendar />
</el-icon>
<el-icon v-if="item.meta.title === '企业管理'">
<OfficeBuilding />
</el-icon>
<el-icon v-if="item.meta.title === 'C端管理'">
<User />
</el-icon>
<el-icon v-if="item.meta.title === '消息中心'">
<ChatDotSquare />
</el-icon>
<span>{{ item.meta.title }}</span>
</el-menu-item>
</template>
</el-menu>
</el-scrollbar>
</div>
<div class="aside">
<div class="scrollbar">
<el-scrollbar>
<el-menu
:default-openeds="['/BusinessManage']"
:default-active="route.path"
active-text-color="#1890FF"
background-color="#fff"
text-color="#000"
router
>
<template v-for="(item, index) in asideRouter[0].children" :key="index">
<el-sub-menu
:index="'/' + item.path"
v-if="item.children && item.children.filter((el) => !el.meta.hide).length > 0"
>
<template #title>
<el-icon v-if="item.meta.title === '门店管理'">
<OfficeBuilding />
</el-icon>
<span> {{ item.meta.title }}</span>
</template>
<el-menu-item
v-for="(ele, idx) in item.children.filter((el) => !el.meta.hide)"
:index="'/' + item.path + '/' + ele.path"
:key="idx"
>
{{ ele.meta.title }}</el-menu-item
>
</el-sub-menu>
<el-menu-item :index="item.redirect || '/' + item.path" v-else-if="!item.meta.hide">
<el-icon v-if="item.meta.title === '工作台'">
<Calendar />
</el-icon>
<el-icon v-if="item.meta.title === '企业管理'">
<OfficeBuilding />
</el-icon>
<el-icon v-if="item.meta.title === 'C端管理'">
<User />
</el-icon>
<el-icon v-if="item.meta.title === '门店审核'">
<Notebook />
</el-icon>
<span>{{ item.meta.title }}</span>
</el-menu-item>
</template>
</el-menu>
</el-scrollbar>
</div>
</div>
</template>
<script setup>
import { routes } from '@/router/index';
import { routes } from '@/router/index'
import { useUserStore } from '@/stores/user'
const route = useRoute();
const isAdmin =
useUserStore().userInfo?.jurisdictions?.findIndex((item) => item.code === 'superadmin') !== -1
const filterRoute = (arr) => {
let tempArr = []
tempArr = arr.filter((item) => !item.meta?.needAdmin)
arr.forEach((item) => {
if (item.children && item.children.length > 0) {
item.children = filterRoute(item.children)
}
})
return tempArr
}
const asideRouter = isAdmin ? routes : filterRoute(routes)
const route = useRoute()
</script>
<style lang="scss" scoped>
.aside {
height: calc(100vh - 60px);
background-color: #fff;
padding-top: 20px;
height: calc(100vh - 60px);
background-color: #fff;
padding-top: 20px;
.scrollbar {
height: calc(100vh - 100px);
}
.scrollbar {
height: calc(100vh - 100px);
}
.el-scrollbar {
background-color: #fff;
.el-scrollbar {
background-color: #fff;
.el-menu-item.is-active {
background-color: rgba(24, 144, 255, 0.1)
}
.el-menu-item.is-active {
background-color: rgba(24, 144, 255, 0.1);
}
.el-menu-item:hover {
background-color: rgba(24, 144, 255, 0.1)
}
.el-menu-item:hover {
background-color: rgba(24, 144, 255, 0.1);
}
:deep(.el-sub-menu__title:hover) {
background-color: rgba(24, 144, 255, 0.1)
}
:deep(.el-sub-menu__title:hover) {
background-color: rgba(24, 144, 255, 0.1);
}
}
}
</style>
</style>

@ -22,19 +22,19 @@ export const routes = [
path: 'BusinessManage',
name: 'BusinessManage',
redirect: '/BusinessManage/EnterpriseManage',
meta: { title: '企业管理' },
meta: { title: '门店管理' },
children: [
{
path: 'EnterpriseManage',
name: 'EnterpriseManage',
component: () => import('@/views/BusinessManage/index.vue'),
meta: { title: '企业列表', hide: true },
meta: { title: '门店列表' },
},
{
path: 'DataManage',
name: 'DataManage',
component: () => import('@/views/BusinessManage/DataManage.vue'),
meta: { title: '资料管理', hide: true },
meta: { title: '分类管理' },
},
],
},
@ -48,7 +48,7 @@ export const routes = [
path: 'MessageCenter',
name: 'MessageCenter',
component: () => import('@/views/MessageCenter/index.vue'),
meta: { title: '消息中心' },
meta: { title: '门店审核', needAdmin: true },
},
],
},
@ -74,6 +74,9 @@ router.beforeEach((to) => {
NProgress.start()
// 如果没有 token ,并且不在白名单中,则从定向到登陆
if (!useUserStore().token && !wihteList.includes(to.path)) return '/login'
if (to.meta.needAdmin && !(useUserStore().userInfo?.jurisdictions?.findIndex(item => item.code === 'superadmin') !== -1)) {
return false
}
})
// 全局后置导航守卫

@ -165,7 +165,7 @@ export function useTableRPC({
RPC.post(remoteDataFn, params)
.then((response) => {
tableData.value = response?.rows || []
total.value = response.count
total.value = response.count || 0
})
.finally(() => {
loading.value = false

@ -1,11 +1,121 @@
<template>
<div class="container">
资料管理
<div>
<div class="title">分类管理</div>
<el-form
ref="searchFormRef"
class="form"
:inline="true"
label-width="98px"
:model="queryParams"
>
<el-form-item label="分类名称" prop="name">
<el-input
clearable
placeholder="请输入"
style="width: 220px"
v-model="queryParams.name"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label-width="100px">
<el-button icon="Search" type="primary" @click="handleQuery"> </el-button>
<el-button icon="RefreshRight" @click="resetQuery"> </el-button>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px">
<el-button icon="Plus" type="primary" @click="add"> </el-button>
</div>
<div ref="tableContainerEl" class="table_border table">
<el-table
ref="tableRef"
:data="tableData"
v-loading="loading"
header-row-class-name="table-header"
>
<el-table-column align="center" label="分类名称" prop="name" />
<el-table-column align="center" label="排序" prop="sort" />
<el-table-column align="center" label="操作">
<template #default="{ row }">
<el-button link type="primary" @click="edit(row)"> </el-button>
<el-button link type="danger" @click="handleDelete(row)"> </el-button>
</template>
</el-table-column>
</el-table>
</div>
<pagination
v-show="total > 0"
:total="total"
v-model:limit="queryParams.limit"
v-model:page="queryParams.page"
@pagination="getList"
/>
<HandleDialog
v-if="isShowDialog"
:type="rowType"
:row="currentRow"
@closed="isShowDialog = false"
@on-success="getList"
/>
</div>
</template>
<script setup>
import { useTableRPC } from '@/utils/hook'
import dayjs from 'dayjs'
import RPC from '@/utils/rpc'
import HandleDialog from './DataManageDialog.vue'
const rowType = ref('')
const currentRow = ref(null)
const add = () => {
isShowDialog.value = true
currentRow.value = null
rowType.value = 'add'
}
const edit = (row) => {
isShowDialog.value = true
rowType.value = 'edit'
currentRow.value = row
}
const {
loading,
tableData,
tableRef,
searchFormRef,
total,
queryParams,
getList,
handleQuery,
resetQuery,
handleDelete,
isShowDialog,
maxHeight,
tableContainerEl,
} = useTableRPC({
queryParams: {},
remoteDataFn: '/content/type/list',
deleteConfig: {
remote: '/content/type/delete',
key: 'id',
message(row) {
return `确认删除 “${row.name}`
},
},
})
getList()
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.title {
font-size: 20px;
font-weight: 700;
margin-bottom: 20px;
}
</style>

@ -0,0 +1,179 @@
<script setup>
import RPC from '@/utils/rpc'
import MapSelector from '@/components/MapSelector.vue'
import UploadFile from '@/components/UploadFile.vue'
const props = defineProps({
type: {
type: String,
required: true,
},
row: [Object],
})
import { VueDraggable } from 'vue-draggable-plus'
const images = ref([])
const emit = defineEmits(['closed', 'onSuccess'])
const dialogVisible = ref(false)
const isAdd = computed(() => props.type === 'add')
onMounted(() => {
dialogVisible.value = true
})
const formRef = ref(null)
const formField = ref({})
const formRules = reactive({
name: [{ required: true, message: '不可为空', trigger: 'change' }],
sort: [{ required: true, message: '不可为空', trigger: 'change' }],
})
const loading = ref(false)
const showMapSelector = ref(false)
function closed() {
emit('closed')
}
const submitForm = async () => {
await formRef.value.validate()
const api = isAdd.value ? '/content/type/create' : '/content/type/update'
const params = {
...formField.value,
images: images.value ? JSON.stringify(images.value) : '[]',
id: isAdd.value ? null : props.row.id,
}
loading.value = true
RPC.post(api, params)
.then((res) => {
ElMessage.success('操作成功')
emit('onSuccess')
emit('closed')
})
.finally(() => {
loading.value = false
})
}
if (!isAdd.value) {
formField.value = props.row
images.value = props.row.images ? JSON.parse(props.row.images) : []
}
const set_video = (val) => {
console.log('val', val)
formField.value.video = val.fullUrl
}
const set_image = (val) => {
images.value.push(val.fullUrl)
}
const delImage = (item) => {
images.value = images.value.filter((ele) => ele !== item)
}
</script>
<template>
<el-dialog
align-center
:title="isAdd ? '新增' : '修改'"
width="80%"
v-model="dialogVisible"
@closed="closed"
:close-on-click-modal="false"
>
<el-form
ref="formRef"
label-position="right"
label-suffix=":"
:model="formField"
:rules="formRules"
v-loading="loading"
label-width="100"
>
<el-form-item prop="name" label="分类名称">
<el-input placeholder="请输入" v-model="formField.name" />
</el-form-item>
<el-form-item prop="sort" label="分类排序">
<el-input-number placeholder="请输入" v-model="formField.sort" />
</el-form-item>
<el-form-item prop="main_image" label="视频">
<video
controls
muted
:src="formField.video"
alt=""
class="video_box"
v-if="formField.video"
/>
<UploadFile v-else accept="video/*" @on-success="set_video"></UploadFile>
</el-form-item>
<el-form-item prop="images" label="图片">
<VueDraggable ghost-class="ghost" target=".sort-target" v-model="images">
<TransitionGroup class="sort-target" name="list" tag="div">
<div class="imgBox" v-for="item in images" :key="item">
<el-image
ref="imageRef"
style="width: 100%; height: 100%"
:src="item"
show-progress
:preview-src-list="images"
fit="cover"
/>
<div class="closeBox" @click="delImage(item)">
<el-icon size="20"><CircleClose /></el-icon>
</div>
</div>
</TransitionGroup>
</VueDraggable>
<UploadFile @on-success="set_image"></UploadFile>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closed"> </el-button>
<el-button type="primary" :loading="loading" @click="submitForm"> </el-button>
</div>
</template>
</el-dialog>
</template>
<style lang="scss" scoped>
.video_box {
width: 370px;
height: 130px;
}
.sort-target {
display: flex;
flex-wrap: wrap;
}
.imgBox {
width: 200px;
height: 200px;
margin-right: 10px;
margin-bottom: 10px;
position: relative;
.closeBox {
position: absolute;
right: 0;
top: 0;
cursor: pointer;
}
}
.list-enter-active,
.list-leave-active {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: scale(0);
}
.ghost {
opacity: 0.5;
}
</style>

@ -1,33 +1,18 @@
<script setup>
import RPC from '@/utils/rpc'
import MapSelector from '@/components/MapSelector.vue'
import UploadFile from '@/components/UploadFile.vue'
const props = defineProps({
type: {
type: String,
required: true,
},
row: [Object],
})
import { VueDraggable } from 'vue-draggable-plus'
const list = ref([
{
name: 'Joao',
id: 1,
},
{
name: 'Jean',
id: 2,
categoryList: {
type: Array,
default: [],
},
{
name: 'Johanna',
id: 3,
},
{
name: 'Juan',
id: 4,
},
])
})
const emit = defineEmits(['closed', 'onSuccess'])
@ -41,7 +26,11 @@ onMounted(() => {
const formRef = ref(null)
const formField = ref({})
const formRules = reactive({})
const formRules = reactive({
store_name: [{ required: true, message: '不可为空', trigger: 'change' }],
latitude: [{ required: true, message: '不可为空', trigger: 'change' }],
longitude: [{ required: true, message: '不可为空', trigger: 'change' }],
})
const loading = ref(false)
const showMapSelector = ref(false)
@ -50,10 +39,27 @@ function closed() {
emit('closed')
}
const submitForm = () => {}
const submitForm = async () => {
await formRef.value.validate()
const api = isAdd.value ? '/store/create' : '/store/update'
const params = {
store: { ...formField.value },
category_ids: formField.value.category_ids,
id: isAdd.value ? null : props.row.id,
}
RPC.post(api, params)
.then((res) => {
ElMessage.success('操作成功')
emit('onSuccess')
emit('closed')
})
.finally(() => {
loading.value = false
})
}
if (!isAdd.value) {
// formField.value = props.row
formField.value = props.row
}
const chooseCoordinates = () => {
@ -66,13 +72,18 @@ const handleCoordinateConfirm = (coordinates) => {
formField.value.longitude = coordinates.longitude
formField.value.latitude = coordinates.latitude
}
const set_main_image = (val) => {
console.log('val', val)
formField.value.main_image = val.fullUrl
}
</script>
<template>
<el-dialog
align-center
:title="isAdd ? '新增' : '修改'"
width="774px"
width="80%"
v-model="dialogVisible"
@closed="closed"
:close-on-click-modal="false"
@ -84,19 +95,71 @@ const handleCoordinateConfirm = (coordinates) => {
:model="formField"
:rules="formRules"
v-loading="loading"
label-width="100"
>
<!-- <VueDraggable ref="el" v-model="list">
<div v-for="item in list" :key="item.id">
{{ item.name }}
</div>
</VueDraggable> -->
<el-form-item prop="store_name" label="门店名称">
<el-input placeholder="请输入" v-model="formField.store_name" />
</el-form-item>
<el-form-item prop="address" label="地址详情">
<el-input placeholder="请输入" v-model="formField.address" />
</el-form-item>
<el-form-item prop="location" label="地区">
<el-input placeholder="请输入" v-model="formField.location" />
</el-form-item>
<el-form-item prop="business_hours" label="工作时间">
<el-input placeholder="请输入" v-model="formField.business_hours" />
</el-form-item>
<el-form-item prop="contact_phone" label="联系电话">
<el-input placeholder="请输入" v-model="formField.contact_phone" />
</el-form-item>
<el-form-item prop="tags" label="分类标签">
<el-input placeholder="请输入,多个以,分隔" v-model="formField.tags" />
</el-form-item>
<el-form-item prop="category_ids" label="分类">
<el-select
clearable
default-first-option
filterable
multiple
placeholder="请选择"
v-model="formField.category_ids"
>
<el-option
v-for="item in categoryList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item prop="main_image" label="门店主图">
<img
:src="formField.main_image"
alt=""
class="main_image_box"
v-if="formField.main_image"
/>
<UploadFile v-else @on-success="set_main_image"></UploadFile>
</el-form-item>
<el-form-item prop="longitude" label="经度">
<el-input-number :controls="false" placeholder="请输入" v-model="formField.longitude" />
<el-input-number
style="width: 200px"
:controls="false"
placeholder="请输入"
v-model="formField.longitude"
/>
</el-form-item>
<el-form-item prop="latitude" label="纬度">
<el-input-number :controls="false" placeholder="请输入" v-model="formField.latitude" />
<el-input-number
style="width: 200px"
:controls="false"
placeholder="请输入"
v-model="formField.latitude"
/>
</el-form-item>
<el-form-item prop="" label="">
<el-button type="primary" @click="chooseCoordinates"></el-button>
</el-form-item>
<el-button type="primary" @click="chooseCoordinates"></el-button>
</el-form>
<template #footer>
@ -116,4 +179,9 @@ const handleCoordinateConfirm = (coordinates) => {
/>
</template>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.main_image_box {
width: 370px;
height: 130px;
}
</style>

@ -1,86 +1,146 @@
<template>
<div>
<div class="title">
企业管理
</div>
<el-form ref="searchFormRef" class="form" :inline="true" label-width="98px" :model="queryParams">
<el-form-item label="个人名称" prop="configName">
<el-input clearable placeholder="请输入" style="width: 220px;" v-model="queryParams.configName" @keyup.enter="handleQuery" />
</el-form-item>
<div>
<div class="title">企业管理</div>
<el-form
ref="searchFormRef"
class="form"
:inline="true"
label-width="98px"
:model="queryParams"
>
<el-form-item label="门店名称" prop="store_name">
<el-input
clearable
placeholder="请输入"
style="width: 220px"
v-model="queryParams.store_name"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label-width="100px">
<el-button icon="Search" type="primary" @keyup.enter.prevent="handleQuery" @keydown.enter.prevent>
搜索
</el-button>
<el-button icon="RefreshRight" @click="resetQuery">
重置
</el-button>
</el-form-item>
</el-form>
<el-form-item label-width="100px">
<el-button icon="Search" type="primary" @click="handleQuery"> </el-button>
<el-button icon="RefreshRight" @click="resetQuery"> </el-button>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px;">
<el-button icon="Plus" type="primary" @click="add">
新建
</el-button>
</div>
<div ref="tableContainerEl" class="table_border table">
<el-table ref="tableRef" :data="tableData" v-loading="loading" header-row-class-name="table-header">
<el-table-column align="center" label="企业名称" prop="name" />
<el-table-column align="center" label="地址详情" prop="name" />
<el-table-column align="center" label="业务类型" prop="name" />
<el-table-column align="center" label="注册日期" prop="name" />
<el-table-column align="center" label="状态" prop="name" />
<el-table-column align="center" label="操作">
<template #default="{ row }">
<el-button link type="primary" @click="edit(row)">
修改
</el-button>
<div style="margin-bottom: 20px">
<el-button icon="Plus" type="primary" @click="add"> </el-button>
</div>
<div ref="tableContainerEl" class="table_border table">
<el-table
ref="tableRef"
:data="tableData"
v-loading="loading"
header-row-class-name="table-header"
>
<el-table-column align="center" label="门店名称" prop="store_name" show-overflow-tooltip />
<el-table-column align="center" label="地区" prop="location" show-overflow-tooltip />
<el-table-column align="center" label="地址详情" prop="address" show-overflow-tooltip />
<el-table-column
align="center"
label="工作时间"
prop="business_hours"
show-overflow-tooltip
/>
<el-table-column align="center" label="电话" prop="contact_phone" show-overflow-tooltip />
<el-table-column align="center" label="业务类型" prop="tags" show-overflow-tooltip />
<el-table-column
align="center"
width="180"
label="注册日期"
prop="created_at"
show-overflow-tooltip
>
<template #default="{ row }">
{{ row.created_at ? dayjs(row.created_at * 1000).format('YYYY-MM-DD HH:mm:ss') : '' }}
</template>
</el-table-column>
<el-table-column align="center" label="状态" prop="status" width="70">
<template #default="{ row }">
<span v-if="row.status == 1"></span>
<span v-if="row.status == 2"></span>
<span v-if="row.status == 3"></span>
</template>
</el-table-column>
<el-table-column align="center" label="驳回原因" prop="review_note" show-overflow-tooltip />
<el-table-column align="center" label="操作" width="120">
<template #default="{ row }">
<el-button link type="primary" @click="edit(row)"> </el-button>
<el-button link type="danger" @click="handleDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-button link type="danger" @click="handleDelete(row)"> </el-button>
</template>
</el-table-column>
</el-table>
</div>
<pagination v-show="total > 0" :total="total" v-model:limit="queryParams.limit" v-model:page="queryParams.page" @pagination="getList" />
<pagination
v-show="total > 0"
:total="total"
v-model:limit="queryParams.limit"
v-model:page="queryParams.page"
@pagination="getList"
/>
<HandleDialog v-if="isShowDialog" :type="rowType" :row="currentRow" @closed="isShowDialog = false" @on-success="getList" />
</div>
<HandleDialog
v-if="isShowDialog"
:type="rowType"
:row="currentRow"
:categoryList="categoryList"
@closed="isShowDialog = false"
@on-success="getList"
/>
</div>
</template>
<script setup>
import { useTableRPC } from '@/utils/hook';
import dayjs from 'dayjs';
import RPC from '@/utils/rpc';
import { useTableRPC } from '@/utils/hook'
import dayjs from 'dayjs'
import RPC from '@/utils/rpc'
import HandleDialog from './HandleDialog.vue'
const rowType = ref('')
const currentRow = ref(null)
const add = () => {
isShowDialog.value = true
currentRow.value = null
rowType.value = 'add'
isShowDialog.value = true
currentRow.value = null
rowType.value = 'add'
}
const edit = (row) => {
isShowDialog.value = true
rowType.value = 'edit'
currentRow.value = row
isShowDialog.value = true
rowType.value = 'edit'
currentRow.value = row
}
const categoryList = ref([])
RPC.post('/content/type/list', { page: 1, limit: -1 }).then((res) => {
categoryList.value = res.rows || []
})
const { loading, tableData, tableRef, searchFormRef, total, queryParams, getList, handleQuery, resetQuery, handleDelete, isShowDialog, maxHeight, tableContainerEl } = useTableRPC({
queryParams: {
},
remoteDataFn: '/store/list',
deleteConfig: {
remote: '/store/delete',
key: 'id',
message(row) {
return `确认删除 “${row.underpainting_name}`
},
const {
loading,
tableData,
tableRef,
searchFormRef,
total,
queryParams,
getList,
handleQuery,
resetQuery,
handleDelete,
isShowDialog,
maxHeight,
tableContainerEl,
} = useTableRPC({
queryParams: {},
remoteDataFn: '/store/list',
deleteConfig: {
remote: '/store/delete',
key: 'id',
message(row) {
return `确认删除 “${row.store_name}`
},
},
})
getList()
@ -88,8 +148,8 @@ getList()
<style lang="scss" scoped>
.title {
font-size: 20px;
font-weight: 700;
margin-bottom: 20px;
font-size: 20px;
font-weight: 700;
margin-bottom: 20px;
}
</style>
</style>

@ -0,0 +1,163 @@
<script setup>
import RPC from '@/utils/rpc'
import MapSelector from '@/components/MapSelector.vue'
import UploadFile from '@/components/UploadFile.vue'
const props = defineProps({
type: {
type: String,
required: true,
},
row: [Object],
categoryList: {
type: Array,
default: [],
},
})
const emit = defineEmits(['closed', 'onSuccess'])
const dialogVisible = ref(false)
const isAdd = computed(() => props.type === 'add')
onMounted(() => {
dialogVisible.value = true
})
const formRef = ref(null)
const formField = ref({})
const formRules = reactive({
store_name: [{ required: true, message: '不可为空', trigger: 'change' }],
latitude: [{ required: true, message: '不可为空', trigger: 'change' }],
longitude: [{ required: true, message: '不可为空', trigger: 'change' }],
})
const loading = ref(false)
const showMapSelector = ref(false)
function closed() {
emit('closed')
}
const submitForm = async () => {
await formRef.value.validate()
const params = { store: { ...formField.value }, category_ids: formField.value.category_ids }
RPC.post('/store/create', params)
}
if (!isAdd.value) {
formField.value = props.row
}
const chooseCoordinates = () => {
showMapSelector.value = true
}
RPC.post('/store/detail', { id: props.row.id })
</script>
<template>
<el-dialog
align-center
:title="isAdd ? '新增' : '修改'"
width="80%"
v-model="dialogVisible"
@closed="closed"
:close-on-click-modal="false"
>
<el-form
ref="formRef"
label-position="right"
label-suffix=":"
:model="formField"
:rules="formRules"
v-loading="loading"
label-width="100"
>
<el-form-item prop="store_name" label="门店名称">
<el-input placeholder="请输入" v-model="formField.store_name" />
</el-form-item>
<el-form-item prop="address" label="详细地址">
<el-input placeholder="请输入" v-model="formField.address" />
</el-form-item>
<el-form-item prop="location" label="地址">
<el-input placeholder="请输入" v-model="formField.location" />
</el-form-item>
<el-form-item prop="business_hours" label="工作时间">
<el-input placeholder="请输入" v-model="formField.business_hours" />
</el-form-item>
<el-form-item prop="contact_phone" label="联系电话">
<el-input placeholder="请输入" v-model="formField.contact_phone" />
</el-form-item>
<el-form-item prop="tags" label="分类标签">
<el-input placeholder="请输入,多个以,分隔" v-model="formField.tags" />
</el-form-item>
<el-form-item prop="category_ids" label="分类">
<el-select
clearable
default-first-option
filterable
multiple
placeholder="请选择"
v-model="formField.category_ids"
>
<el-option
v-for="item in categoryList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item prop="main_image" label="门店主图">
<img
:src="formField.main_image"
alt=""
class="main_image_box"
v-if="formField.main_image"
/>
<UploadFile v-else @on-success="set_main_image"></UploadFile>
</el-form-item>
<el-form-item prop="longitude" label="经度">
<el-input-number
style="width: 200px"
:controls="false"
placeholder="请输入"
v-model="formField.longitude"
/>
</el-form-item>
<el-form-item prop="latitude" label="纬度">
<el-input-number
style="width: 200px"
:controls="false"
placeholder="请输入"
v-model="formField.latitude"
/>
</el-form-item>
<el-form-item prop="" label="">
<el-button type="primary" @click="chooseCoordinates"></el-button>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closed"> </el-button>
</div>
</template>
</el-dialog>
<!-- 地图坐标选择器 -->
<MapSelector
v-model="showMapSelector"
:longitude="formField.longitude"
:latitude="formField.latitude"
@confirm="handleCoordinateConfirm"
/>
</template>
<style lang="scss" scoped>
.main_image_box {
width: 370px;
height: 130px;
}
</style>

@ -1,11 +1,182 @@
<template>
<div>
消息中心
<div>
<div class="title">门店审核</div>
<el-form
ref="searchFormRef"
class="form"
:inline="true"
label-width="98px"
:model="queryParams"
>
<el-form-item label="门店名称" prop="store_name">
<el-input
clearable
placeholder="请输入"
style="width: 220px"
v-model="queryParams.store_name"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label-width="100px">
<el-button
icon="Search"
type="primary"
@keyup.enter.prevent="handleQuery"
@keydown.enter.prevent
>
搜索
</el-button>
<el-button icon="RefreshRight" @click="resetQuery"> </el-button>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px">
<el-button icon="Plus" type="primary" @click="add"> </el-button>
</div>
<div ref="tableContainerEl" class="table_border table">
<el-table
ref="tableRef"
:data="tableData"
v-loading="loading"
header-row-class-name="table-header"
>
<el-table-column align="center" label="门店名称" prop="store_name" show-overflow-tooltip />
<el-table-column align="center" label="地区" prop="location" show-overflow-tooltip />
<el-table-column align="center" label="地址详情" prop="address" show-overflow-tooltip />
<el-table-column
align="center"
label="工作时间"
prop="business_hours"
show-overflow-tooltip
/>
<el-table-column align="center" label="电话" prop="contact_phone" show-overflow-tooltip />
<el-table-column align="center" label="业务类型" prop="tags" show-overflow-tooltip />
<el-table-column
align="center"
width="180"
label="注册日期"
prop="created_at"
show-overflow-tooltip
>
<template #default="{ row }">
{{ row.created_at ? dayjs(row.created_at * 1000).format('YYYY-MM-DD HH:mm:ss') : '' }}
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="150">
<template #default="{ row }">
<el-button link type="primary" @click="goDetail(row)"> </el-button>
<el-button link type="primary" @click="review(row.id, 2)"> 通过 </el-button>
<el-button link type="danger" @click="review(row.id, 3)"> 驳回 </el-button>
</template>
</el-table-column>
</el-table>
</div>
<pagination
v-show="total > 0"
:total="total"
v-model:limit="queryParams.limit"
v-model:page="queryParams.page"
@pagination="getList"
/>
<HandleDialog
v-if="isShowDialog"
:type="rowType"
:row="currentRow"
:categoryList="categoryList"
@closed="isShowDialog = false"
@on-success="getList"
/>
</div>
</template>
<script setup>
import { useTableRPC } from '@/utils/hook'
import dayjs from 'dayjs'
import RPC from '@/utils/rpc'
import HandleDialog from './HandleDialog.vue'
const rowType = ref('')
const currentRow = ref(null)
const add = () => {
isShowDialog.value = true
currentRow.value = null
rowType.value = 'add'
}
const goDetail = (row) => {
isShowDialog.value = true
rowType.value = 'edit'
currentRow.value = row
}
const review = (id, status) => {
if (status === 2) {
ElMessageBox.confirm('确认通过?', '通过', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
RPC.post('/store/review', { id, status }).then(() => {
ElMessage.success('操作成功')
getList()
})
})
.catch(() => {})
} else {
ElMessageBox.prompt('请输入驳回原因', '驳回', {
confirmButtonText: '确认',
cancelButtonText: '取消',
inputErrorMessage: '请输入驳回原因',
inputValidator: (value) => {
return value.trim() !== ''
},
})
.then(({ value }) => {
RPC.post('/store/review', { id, status, note: value }).then(() => {
ElMessage.success('操作成功')
getList()
})
})
.catch(() => {})
}
}
const {
loading,
tableData,
tableRef,
searchFormRef,
total,
queryParams,
getList,
handleQuery,
resetQuery,
handleDelete,
isShowDialog,
maxHeight,
tableContainerEl,
} = useTableRPC({
queryParams: {},
remoteDataFn: '/store/pending_list',
// deleteConfig: {
// remote: '/store/pending_list',
// key: 'id',
// message(row) {
// return ` ${row.underpainting_name}`
// },
// },
})
getList()
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.title {
font-size: 20px;
font-weight: 700;
margin-bottom: 20px;
}
</style>

@ -62,6 +62,10 @@ export default defineConfig({
target: 'https://lift-dev.ruixininfo.com/',
changeOrigin: true,
},
'/filestransfer': {
target: 'https://lift-dev.ruixininfo.com/',
changeOrigin: true,
},
},
},
})

Loading…
Cancel
Save